信号处理函数的注册
信号处理函数的注册不只一种方法,分为入门版和高级版
1.入门版: 函数 signal
2.高级版:函数 sigection
信号处理发送函数
信号发送函数也不止一个,同样分为入门版和高级版
1.入门版: 函数 kill
2.高级版: 函数 sigqueue
sigaction函数
作用
sigaction函数是一个系统调用,可以用来查询或设置信号处理方式。
头文件
#include <signal.h>
函数原型
int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
参数解读
signum | 参数指出要捕获的信号类型 |
act | 参数指定新的信号处理方式,struct sigaction类型如果不为空说明需要对该信号有新的配置(函数原型是指针,输入参数时应输入地址) |
oldact | 备份 1.如果不为空,那么可以对之前的信号配置进行备份,以方便之后进行恢复 2.如果为空,直接输入NULL |
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:与前一章节中的handler相同,代表新的信号处理函数,但不能接受额外数据。
sa_sigaction:信号处理程序,代表新的信号处理函数,能够接受额外数据,需配合sigqueue函数
int | 这里的int一般是信号函数类型(编号),如int signum |
siginfo_t * | 用于接受信号,功能比较多样化,如siginfo_t *info |
void * | 接收到信号所携带的额外数据,是否为NULL可以判断是否携带额外数据,如void *context |
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_mark:用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置
调用该函数后,会在捕捉函数调用前设置为阻塞,并在捕捉函数返回时恢复默认原有设置。这样的目的是,在调用信号处理函数时,就可以阻塞默写信号了。在信号处理函数被调用时,操作系统会建立新的信号阻塞字,包括正在被递送的信号。因此,可以保证在处理一个给定信号时,如果这个种信号再次发生,那么他会被阻塞到对之前一个信号的处理结束为止
sa_flags:用来设置信号处理的其他相关操作,下列的数值可用
SA_RESETHAND | 当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL |
SA_RESTART | 如果信号中断了进程的某个系统调用,则系统自动启动该系统调用 |
SA_NODEFER | 一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号 |
但由于我们需要接受数据,所以这里我们经常使用的数值是SA_SIGINFO
sigqueue函数
作用
在队列中向指定进程发送一个信号和数据
头文件
#include <signal.h>
函数原型
int sigqueue(pid_t pid, int sig, const union sigval value);
参数解读
pid | 信号发送目标位置的进程号,可用ps查看或者直接调用getpid()打印出来 |
sig | 所发送的信号类型,如SIGUUSR1 |
value | 信号附带的数据,即传送的内容是什么,是一个联合体 |
union sigval联合体
union sigval
{
int sival_int;//传递的数据可以是整型数
void *sival_ptr;//指向要传递的信号参数
};
注意事项
1、使用 sigaction 函数安装信号处理程序时,需先写入 SA_SIGINFO 标志才可以接受数据。
2、sigaction 结构体中的sa_sigaction成员提供了信号捕捉函数。如果实现的sa_handler成员,那么将无法获取额外携带的数据。
3、sigqueue 函数只能把信号发送给单个进程,可以使用 value 参数向信号处理程序传递整数值或者指针值。
代码示例
sigcation.c
#include <stdio.h>
#include <signal.h>
void handler(int signum,siginfo_t *info,void *context)
{
printf("the signum is %d\n",signum);
if(context != NULL)//信号携带内容,将内容打印出来
{
printf("get context is %d\n",info->si_int);//内容为整型数,整型数打印有两种方法 第一种是直接打印整型数,第二种是调用共用体中的整型数打印
printf("get context is %d\n",info->si_value.sival_int);
printf("send pid is %d\n",info->si_pid);
}
}
int main()
{
struct sigaction act;
printf("pid is %d\n",getpid());
act.sa_sigaction = handler;//信号处理函数调用
act.sa_flags = SA_SIGINFO;//设置为可接收数据
sigaction(SIGUSR1,&act,NULL);//第二个参数是指针,应输入地址,第三个参数是备份,不需要则写入NULL
while(1);
return 0;
}
sigqueue.c
#include <stdio.h>
#include <signal.h>
int main(int agrc,char **agrv)
{
int pid = atoi(agrv[1]);
int signum =atoi(agrv[2]);
union sigval value;
value.sival_int = 999;//信号内容设置为999
sigqueue(pid,signum,value);
printf("pid is %d\n",getpid());
puts("done");
return 0;
}
先将sigaction.c的进程号打印出来后会等待接受数据,接着调用sigqueue.c将其自身进程号打印出来,并做信号处理,将携带内容发送至sigaction.c,sigaction.c接受到数据后将数据打印出来。实现了在一端发送信号,在另一端接收信号。