本文承接上文Liunx系统编程篇—进程通信(五)信号(一)(原理、概述、创建、实战)(kill、signal)上文对于信号的一些理论,本文就不再赘述。
信号处理函数的注册
入门版:函数signal
高级版:函数sigaction
信号处理发送函数
1.入门版:kill
2.高级版:sigqueue
对于入门版的信号处理API的重点在于动作,但kill 函数发送的信号是无法携带数据。
对于高级版的信号处理API的==重点信号携带的信息
sigaction 是一个系统调用,可以用来查询或设置信号处理方式。
#include
int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
signum:参数指出要捕获的信号类型。
act:参数指定新的信号处理方式,struct sigaction类型如果不为空说明需要对该信号有新的配置。
oldact:备份,如果不为空,那么可以对之前的信号配置进行备份,以方便之后进行恢复。
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此参数和signal()的参数handler相同,代表新的信号处理函数
关于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_signo 和 si_code 是必须实现的两个成员。可以通过这个结构体获取到信号的相关信息。
关于发送过来的数据是存在两个地方的,sigval_t si_value这个成员中有保存了发送过来的信息;
同时,在si_int或者si_ptr成员中也保存了对应的数据。
sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置
sa_mask 成员,设置在其的信号集中的信号,会在捕捉函数调用前设置为阻塞,并在捕捉函数返回时恢复默认原有设置。这样的目的是,在调用信号处理函数时,就可以阻塞默写信号了。在信号处理函数被调用时,操作系统会建立新的信号阻塞字,包括正在被递送的信号。因此,可以保证在处理一个给定信号时,如果这个种信号再次发生,那么他会被阻塞到对之前一个信号的处理结束为止。
sa_flags 用来设置信号处理的其他相关操作,下列的数值可用:
SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号
在队列中向指定进程发送一个信号和数据
#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。
#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;
}
#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;
}