温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Linux捕捉信号机制之(signal,kill)、(sig

发布时间:2020-07-03 08:26:06 来源:网络 阅读:17455 作者:SherryX 栏目:系统运维

linux下公有64个信号,kill -l 查看一下:
Linux捕捉信号机制之(signal,kill)、(sig
可以看到,缺少了32、33两个未知信号,从这里分界,前面31个信号是不可靠信号,后面的是可靠信号。当进程发生阻塞的时候(一下子发送很多信号),不可靠信号容易丢失。如何去验证呢?可以在2(不可靠信号)号信号和34(可靠进程)号信号屏蔽期间,不断向某个进程发送这两个信号,待解除屏蔽后,观察是否丢失。这里测试的时候,要注意一下,9-SIGKILL 19-SIGSTOP 31 32 这4个信号是不能被捕获的,遍历以下所有信号就可以发现了。
<br>

信号集

首先,讲一下信号集,顾名思义,存放信号的集合。
信号集的操作函数有以下几个,具体使用,后面再说。
Linux捕捉信号机制之(signal,kill)、(sig

signal

第一种是利用signal,kill函数。

       #include < signal.h>
       typedef void (*sighandler_t)(int);
       sighandler_t signal(int signum, sighandler_t handler);

signal()有两个参数:信号编号和处理函数(sighandler_t是一个函数指针),返回值也是一个sighandler_t类型的,这里返回的是之前的信号处理函数。
信号处理函数是一个带int参数,返回值为void的函数。handler也可以是两个特殊的值:SIG_IGN 屏蔽该信号 SIG_DFL 恢复默认行为

       #include < sys/types.h>
       #include < signal.h>
       int kill(pid_t pid, int sig);

kill()的作用是把信号sig发送给进程pid。

#include <signal.h>
 int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

功能:读取过更改进程的信号屏蔽字。
返回值:成功为0,失败为-1
参数:如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出(就是将原来的信号屏蔽字备份到oset);set是更改进程的信号屏蔽字,how指示如何修改。how的可选值如下:
Linux捕捉信号机制之(signal,kill)、(sig
代码如下:

// 3.不可靠信号的丢失
void handler(int no)
{
    printf("received a signal: %d\n",no);
}

int main()
{
    pid_t pid;
    sigset_t set;
    sigset_t oset;
    int i;

    sigemptyset(&set);          //清空
    sigaddset(&set,2);          //添加2号信号
    sigaddset(&set,34);         //添加34号信号
    for(i = 1 ; i <= 64 ; i++)  //查看集合状态
    {
        if(sigismember(&set,i) == 1)
        {
            printf("1");
        }
        else
        {
            printf("0");
        }
    }
    printf("\n");
    sigprocmask(SIG_SETMASK,&set,&oset);     //将这个集合设置为这个进程的阻塞信号集

    //绑定信号
    signal(2,handler);
    signal(34,handler);

    sleep(50);  //在此期间,向该进程发送多次2、34号信号
    sigprocmask(SIG_SETMASK,&oset,NULL);//解除绑定

    while(1)
    {
    }
    return 0;
}

在另一个终端(20s内),发送信号:
Linux捕捉信号机制之(signal,kill)、(sig
结果,可以看到,不可靠信号,只收到了一次:
Linux捕捉信号机制之(signal,kill)、(sig

sigaction

第二种是sigacton函数。

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

功能:用于改变进程接收到特定信号后的行为。
参数:signum 除了SIGKILL 和SIGSTOP(为这两个信号定义自己的处理函数,将导致信号安装错误);
第二个参数是指向结构sigaction的一个实例的指针,在结构sigaction的实例中,制定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理;
old用来保存原来对信号的处理,可以为NULL。
返回值:成功为0,失败-1
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);
           };

参数:
前两个参数sa_handler和sa_sigaction都是自定义信号处理函数,同样,sa_handler有两个默认值:SIG_DFL和SIG_IGN。区别在于sa_sigaction是为实时信号而设定的(也支持非实时信号),第二个参数是指向siginfo_t结构的指针,结构中包含信号携带的数据值,第三个参数暂时没有使用(POSIX没有规范使用该指针的标准);
sa_mask是屏蔽信号集;
sa_flags有以下几个值:
Linux捕捉信号机制之(signal,kill)、(sig
重点掌握2个:一个是设置为0,表示默认属性;一个是设置为SA_SIGINFO,那么此时处理函数不再使用sa_handler,而是sa_sigaction.
那么sa_sigaction的结构体参数长什么样呢?

           siginfo_t {
               int      si_signo;    /* Signal number */
               int      si_errno;    /* An errno value */
               int      si_code;     /* Signal code */
               pid_t    si_pid;      /* Sending process ID */
               uid_t    si_uid;      /* Real user ID of sending process */
               int      si_status;   /* Exit value or signal */
               clock_t  si_utime;    /* User time consumed */
               clock_t  si_stime;    /* System time consumed */
               sigval_t si_value;    /* Signal value */
               int      si_int;      /* POSIX.1b signal */
               void    *si_ptr;      /* POSIX.1b signal */
               void    *si_addr;     /* Memory location which caused fault */

天哪,肯定记不住。不需要记,用到的时候,查一下就好了。
这里掌握两个,一个是si_signum,不用说了吧,一个是si_value。这是什么?这是在发送信号时携带的一个数据,数据类是sigval,这是一个联合体。

typedef union sigval
 { 
    int sival_int; 
    void *sival_ptr; 
}sigval_t; 

sigqueue函数

  #include < signal.h>
  int sigqueue(pid_t pid, int sig, const union sigval value);

sigqueue()类似于之前的kill()。是用来发送信号的,主要针对有带参的信号,与sigaction()配合使用。
第三个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。
具体代码如下:

void handler(int signo,siginfo_t *resdata,void *unknowp)
{
    printf("signo=%d\n",signo);
    printf("return data :%d\n",resdata->si_value.sival_int);
}

int main()
{
    int i = 5;
    pid_t pid = 0;
    pid = fork();
    if(pid == -1)
    {
        perror("create fork");
        return -1;
    }
    else if(pid == 0)
    {
        sleep(1);
        //向父进程发送带整型数据的信号
        union sigval sigvalue;
        sigvalue.sival_int = 111;
        //发送信号
        while(i--)
        {
            sigqueue(getppid(),2,sigvalue);
            printf("send signal:2 success!\n");
            sigqueue(getppid(),34,sigvalue);
            printf("send signal:34 success!\n");
        }

    }
    else
    {

        struct sigaction act;
        //初始化sa_mask
        sigemptyset(&act.sa_mask);
        act.sa_sigaction=handler;
        //一旦使用了sa_sigaction属性,那么必须设置sa_flags属性的值为SA_SIGINFO
        act.sa_flags=SA_SIGINFO;

        //注册信号
        sigaction(2,&act,NULL);
        sigaction(34,&act,NULL);

    }

    while(1)
    {
    }
}

运行结果:
Linux捕捉信号机制之(signal,kill)、(sig
同样,可以看到2号信号只收到了1次。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI