进程的信号和signal() sigactoin()函数

使用ctrl + c杀死进程

在Linux中假设我们写了一个死循环的程序,然后运行起来.那么这个程序一直就在执行循环体内的内容.

如果让这个程序停下来呢?我们通常都会用 ctrl + c “杀死”这个程序.

其实ctrl+c是一个硬件中断,当cpu接收到这个硬件中断后,会停止执行用户态当前的代码.

cpu从进程的用户态切换至内核态, 这时候驱动程序将 ctrl +c 翻译为一个SIGINT信号

此时操作系统将该信号记录在进程PCB中( 可以理解为向进程发送了此信号)

当进程的代码从内核态将要返回用户态时,会检查该进程的PCB中信号的状态

如果发现此时SIGINT信号待处理,那么就会去处理这个信号,而这个信号作用就是终止进程.

进程就不会返回到用户态而直接被结束掉了.

了解以上过程后,我们可以了解处到信号是给进程下达特定行为的一种工具.

而且不同的信号,进程都知道该信号的处理方式.

这就像路边的交通灯,当红灯亮的时候 ,采取的动作是刹车.

红灯就是一种信号,而该信号处理方式就是 刹车. 信号和处理方式也应该是事先约定好的.

信号的产生方式

1.通过在终端下按键产生,比如之前提到过的 ctrl + c
2.硬件异常,例如除零 (SIGFPE),访问非法内存(SIGSEGV)
3.在终端下使用kill命令 或者在程序中使用kill()函数
    a.kill(pid_t pid, int signo) 向指定进程发送某个信号
    b.raise(int signo) 给自己发送信号
    c.abort(void) 给自己发送异常中止函数

信号的状态

  1. 信号的状态有 阻塞(block),未决(pending).抵达(deliver)
  2. 未决就是该信号已经发送,但没有被处理
  3. 抵达就是该信号可以被处理了(handler)
    1. 处理方式有: 默认,自定义(hanler),忽略
  4. 如果一个信号被阻塞,那么即使它是未决状态,那么它也不会被抵达

可以用这么一张表来表示

无
横向看,每个信号都对应一个BLOCK,pending,和handler状态;

siganl() 函数捕捉信号

我们首先使用使用 sigpromask() 阻塞 sigint信号, 当sigint信号pending时,过五秒解除对sigint的阻塞,那么此刻信号就会被抵达

我们又可以使用signal()函数捕捉抵达的信号,并自定义处理该信号所产生的行为.

       #include <signal.h>

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);

其中参数部分第二个就是上图中最后一个handler表, 这里hanlder是一个函数指针也是回调函数,他的地址就是自定义信号处理函数void (*sighandler_t)(int);

可以看到处理函数可以带一个int参数

还使用到了 使用到了信号集和一系列关于信号集操作的函数

信号集就是一个集合,表示一个集合里所有信号(1到31号普通信号信号)的状态

信号集操作函数可以改变信号集中具体或者某些信号的状态

并将1到31号信号实事的打印在屏幕上 更直观一些

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
sigset_t s,o,p;
//打印信号
void printSIG(sigset_t* p)
{
    int i = 0;
    for(;i < 32; i++)
    {
       if( sigismember(p,i) )
       {
           printf("1 ");
           fflush(stdout);
       }
       else
       {
           printf("0 ");
           fflush(stdout);
       }
    }
    printf("\n");
}
void hander()
{
    printf("i catch you ctrl-c !!!,you can't do it forevor\n");
}
int main()
{
    int block_cnt = 0;
    sigemptyset(&s);
    sigemptyset(&p);
    sigaddset(&s,SIGINT);
    sigprocmask(SIG_SETMASK,&s,&o);
    signal(SIGINT,hander);
    while(1)
    {
        if(block_cnt == 5)
        {

            printf("you are unfrezee\n");
            sigprocmask(SIG_SETMASK,&o,NULL);
        }
        sleep(1);
        sigpending(&p);
        printSIG(&p);
        //接收到信号五秒后解除信号block
        if(sigismember(&p,SIGINT))
        {
            block_cnt++;;
            printf("%d\n",block_cnt);
            printf("warning!!  you are trying unblocking!!\n");
        }
    }
}

sigactoin()函数

其实对于信号抵达后的处理,推荐你使用sigactoin()函数,而不是signal()函数

在早些的版本中,signal()信号只能对信号进行一次处理,处理完成后该信号的处理方式就变成了原来的处理方式

但如今sinal()函数也是由sigactoin函数实现的





SYNOPSIS
   #include <signal.h>
   int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);



其中结构体 sigaction 如下

           struct sigaction {
           void     (*sa_handler)(int);//处理函数
           void     (*sa_sigaction)(int, siginfo_t *, void *);//处理实时信号的函数
           sigset_t   sa_mask;//希望在屏蔽处理的信号同时还屏蔽哪些信号
           int        sa_flags;
           void     (*sa_restorer)(void);
       };

使用sigactoin捕获SIGINT信号

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void handler(int signo)
{
    printf("i've got %d sig\n",signo);
}
struct sigaction sig;
struct sigaction old;
int main()
{
    sig.sa_handler = handler;
    sigaction(SIGINT,&sig,&old);
    while(1)
    {
        sleep(1);
        printf("wait ctrl + c \n");
    }
    return 0;
}

信号在什么时候抵达

如何信号没有被阻塞而且已经pending了,当进程从用户切换回内核态(一个进程在执行过程会多次从用户态转入内核态)
然后从内核态切回用户态前就会对进程task_struct中pending的信号进行处理,例如如果信号处理函数是自定义的
那么就会从内核切换回用户态函数执行的地址,执行玩又返回内核态,最后切换至程序原本执行流的地方
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值