Liunx系统编程篇—进程通信(五)信号(二)(sigaction、sigqueue)

本文承接上文Liunx系统编程篇—进程通信(五)信号(一)(原理、概述、创建、实战)(kill、signal)上文对于信号的一些理论,本文就不再赘述。

本文重点是讲解:sigactionsigqueue两个函数,分别用于信号处理函数的注册、信号处理发送函数是signal和kill的升级版。

常用API

信号处理函数的注册
入门版:函数signal
高级版:函数sigaction

信号处理发送函数
1.入门版:kill
2.高级版:sigqueue
对于入门版的信号处理API的重点在于动作,但kill 函数发送的信号是无法携带数据。
对于高级版的信号处理API的==重点信号携带的信息

sigaction函数

sigaction 是一个系统调用,可以用来查询或设置信号处理方式。

包含的头文件

#include

函数原型:

int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);

signum:参数指出要捕获的信号类型。
act:参数指定新的信号处理方式,struct sigaction类型如果不为空说明需要对该信号有新的配置。
oldact:备份,如果不为空,那么可以对之前的信号配置进行备份,以方便之后进行恢复。

struct sigaction结构体介绍

struct sigaction {
   void (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作
   void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
   sigset_t sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
   int sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
 };
//回调函数句柄sa_handler、sa_sigaction只能任选其一
sa_handler

sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数

void (*sa_sigaction)(int, siginfo_t *, void *);

关于void (*sa_sigaction)(int, siginfo_t *, void *);处理函数来说还需要有一些说明。
void* 是接收到信号所携带的额外数据;
而struct siginfo这个结构体主要适用于记录接收信号的一些相关信息。

 siginfo_t {
               int      si_signo;    /* Signal number */
               int      si_errno;    /* An errno value */
               int      si_code;     /* Signal code */
               int      si_trapno;   /* Trap number that caused
                                        hardware-generated signal
                                        (unused on most architectures) */
               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 */
               int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
               int      si_timerid;  /* Timer ID; POSIX.1b timers */
               void    *si_addr;     /* Memory location which caused fault */
               int      si_band;     /* Band event */
               int      si_fd;       /* File descriptor */
}

其中的成员很多,si_signosi_code 是必须实现的两个成员。可以通过这个结构体获取到信号的相关信息。
关于发送过来的数据是存在两个地方的,sigval_t si_value这个成员中有保存了发送过来的信息;
同时,在si_int或者si_ptr成员中也保存了对应的数据。

sa_mask

sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置
sa_mask 成员,设置在其的信号集中的信号,会在捕捉函数调用前设置为阻塞,并在捕捉函数返回时恢复默认原有设置。这样的目的是,在调用信号处理函数时,就可以阻塞默写信号了。在信号处理函数被调用时,操作系统会建立新的信号阻塞字,包括正在被递送的信号。因此,可以保证在处理一个给定信号时,如果这个种信号再次发生,那么他会被阻塞到对之前一个信号的处理结束为止。

sa_flags

sa_flags 用来设置信号处理的其他相关操作,下列的数值可用:
SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号

sigqueue函数

在队列中向指定进程发送一个信号和数据

包含的头文件

#include 

函数原型:

int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {
   int   sival_int;
   void *sival_ptr;
 };

pid:发给谁,是目标进程的进程号
sig:发的是什么信号,是信号代号
value:发送的消息(int或者char*),是一个联合体,表示信号附带的数据,附带数据可以是一个整数也可以是一个指针,有如下形式:

union sigval {
	int sival_int;
	void *sival_ptr;//指向要传递的信号参数
};value

使用这个函数之前,必须要有几个操作需要完成

1、使用 sigaction 函数安装信号处理程序时,制定了 SA_SIGINFO 的标志=。
2、sigaction 结构体中的sa_sigaction成员提供了信号捕捉函数。如果实现的sa_handler成员,那么将无法获取额外携带的数据。
3、sigqueue 函数只能把信号发送给单个进程,可以使用 value 参数向信号处理程序传递整数值或者指针值。
4、、sigqueue 函数不但可以发送额外的数据,还可以让信号进行排队(操作系统必须实现了 POSIX.1的实时扩展),对于设置了阻塞的信号,使用 sigqueue 发送多个同一信号,在解除阻塞时,接受者会接收到发送的信号队列中的信号,而不是直接收到一次。但是,信号不能无限的排队,信号排队的最大值受到SIGQUEUE_MAX的限制,达到最大限制后,sigqueue 会失败,errno 会被设置为 EAGAIN。

实例:

demo.c

#include
#include 
/*信号处理函数*/
void handler(int signum, siginfo_t *info, void *context)
{
        printf("get signum %d\n",signum);
        if(context!=NULL){//如果有内容
                printf("get data=%d\n",info->si_int);
                printf("get data=%d\n",info->si_value.sival_int);
        }
}
int main()
{
        struct sigaction act;
        act.sa_sigaction = handler;//信号处理程序,能够接受额外数据和sigqueue配合使用
        act.sa_flags = SA_SIGINFO;//影响信号的行为SA_SIGINFO表示能够接受数据
        sigaction(SIGUSR1,&act,NULL);
        sigaction(SIGINT,&act,NULL);
        while(1);
        return 0;
}

send.c

#include 
#include 
int main(int argc,char **argv)
{
        int pid = atoi(argv[2]);
        int signum = atoi(argv[1]);
        union sigval value;
        value.sival_int = 100;
        sigqueue(pid,signum,value);//信号处理发送函数
        printf("done\n");
        return 0;
}

实验结果:

在这里插入图片描述

你可能感兴趣的:(Liunx系统编程篇,linux,嵌入式,经验分享)