一、函数原型
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
参数:
- signum:要捕获的信号
- act:接收到信号之后对信号进行处理的结构体
- oldact:接收到信号之后,保存原来对此信号处理的各种方式与信号(可用来做备份)。如果不需要备份,此处可以填NULL
返回值:
- 成功时:返回0
- 出错时:返回-1,并将errno设置为指示错误
与signal的不同,有哪些新功能?
- signal只能捕获信号,对信号进行处理。但是不能获取信号的其它信息
- sigaction可以使用sigaction结构体的sa_handler函数对信号进行处理(此处等同于signal函数),也可以使用sa_sigaction函数查看信号的各种详细信息
- 并且sigaction函数还可以通过sa_mask、sa_flags对信号处理时进行很多其他操作
二、struct sigaction结构体
struct sigaction
{
void (*sa_handler)(int signum);
void (*sa_sigaction)(int signum, siginfo_t *info, void *p);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
sa_handler成员:
对捕获的信号进行处理的函数,函数参数为sigaction函数的参数1信号(概念上等同于单独使用signal函数)
也可以设置为后面两个常量:
常数SIG_IGN(向内核表示忽略此信号)
常数SIG_DFL(表示接到此信号后的动作是系统默认动作)
sa_sigaction成员:
当sa_flags成员是SA_SIGINFO标志时,就调用此函数,可以来获取该信号的很多详细信息(而不是用来对信号进行处理)
signum:信号编号
siginfo:结构体中包含信号产生的各种详细信息
- si_value(重点):传递的信息。通过sigqueue函数(union sigval),获取整型值si_value.sival_int或者si_value.sival_ptr传递一块内存,两者取一。
typedef union sigval { int sival_int; void *sival_ptr; }sigval_t;
# siginfo->si_value.sival_int = siginfo->si_int //相等 # siginfo->si_value.sival_ptr = siginfo->si_ptr //相等
- void *p:
sa_handler和sa_sigaction函数一次只能使用其中1个:
sa_sigaction函数,sa_flags必须为SIGINFO
# 伪代码示意 void my_handler(int signum, siginfo *siginfo, void *p); ********************************************************************** struct sigaction act, oact; act.sa_handler = my_handler; sigemptyset(&act.sa_mask); act.sa_flags = SIGINFO; //使用3参函数的关键 sigaction(SIGUSER2, &act, &oact);
sigset_t sa_mask成员:
- 功能:sa_mask是一个信号集,当接收到某个信号,并且调用sa_handler函数对信号处理之前,把该信号集里面的信号加入到进程的信号屏蔽字当中,当sa_handler函数执行完之后,这个信号集中的信号又会从进程的信号屏蔽字中移除
- 为什么这样设计??这样保证了当正在处理一个信号时,如果此种信号再次发生,信号就会阻塞。如果阻塞期间产生了多个同种类型的信号,那么当sa_handler处理完之后。进程又只接受一个这种信号
- 即使没有信号需要屏蔽,也要初始化这个成员(sigemptyset()),不能保证sa_mask=0会做同样的事情
- sigset_t结构体
sa_flags成员:指定信号如何处理
SA_INTERRUPT 由此信号中断的系统调用不自动重启动 SA_NOCLDSTOP 若signo是SIGCHLD:
当一子进程停止(暂停时)时(作业控制), 不产生此信号
当一子进程终止时,仍旧产生此信号
若已设置此标志,则当停止的进程继续运行时,作为XSI扩展,不产生SIGCHLD信号
(参照的下面的SA_NOCLDWAIT选项)
SA_NOCLDWAIT 若signo是SIGCHLD,则当调用进程的子进程终止时, 不创建僵死进程
若调用进程在后面调用wait,则阻塞到它所有子进程都终止,此时返回-1,errno设置为ECHILD
SA_NODEFER 当捕捉到此信号时,在执行其信号捕捉函数时,系统不自动阻塞此信号(除非sa_mask成员包括了此信号)
注意,此种类型的操作对应于早期的不可靠信号
SA_NOMASK 同SA_NODEFER SA_ONSTACK 若用sigaltstack已声明了一替换栈,则此信号递送给替换栈上的进程 SA_STACK 同SA_ONSTACK SA_RESETHAND 在此信号捕捉函数的入口处,将此信号的处理方式重置为SIG_DFL,并清除SA_SIGINFO标志
注意,此种类型的信号对应于早期的不可靠信号。但是不能重置SIGILL和SIGTRAP信号的配置
设置此标志使sigaction的行为如同设置了SA_NODEFER标志
SA_ONESHOT 同SA_RESETHAND SA_RESTART 由此信号中断的系统调用自动重启动 SA_SIGINFO 此选项对信号处理程序提供了附加信息:一个指向siginfo结构的指针以及一个指向进程上下文标识符的
说明:
关于SA_INTERRUPT、SA_RESTART说明:
某些早期系统(如SunOS)定义了SA_INTERRUPT标志,这些系统的默认方式是重新启动被中断的系统调用,而制定此标志则使系统调用被中断后不再重新启动。Linux定义SA_INTERRUPT标志,以便与使用该标志的应用程序兼容。但是,如若信号处理程序是用sigaction设置的,那么其默认方式是不重新启动系统调用
Single UNIX Specifiaction的XSI扩展规定,除非说明了SA_RESTART标志,否则sigaction函数不再重启动被中断的系统调用
三、代码实现
信号接收模块:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> //void (*sa_sigaction)(int, siginfo_t *, void *); //int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); void cym_handler(int signum, siginfo_t *info, void *p) { printf("n = %d\n", signum); printf("info->si_val.sival_int = %d\n", info->si_value.sival_int); printf("info->si_int = %d\n", info->si_int); } int main() { struct sigaction newsa; struct sigaction oldsa; sigset_t newset; sigset_t oldset; newsa.sa_sigaction = cym_handler; sigemptyset(&newsa.sa_mask); newsa.sa_flags = SA_SIGINFO; sigaddset(&newsa.sa_mask, SIGUSR2); sigprocmask(SIG_BLOCK, &newsa.sa_mask, &oldset); //阻塞方式 if (sigaction(SIGUSR2, &newsa, &oldsa) < 0) { perror("sigaction()"); exit(1); } while (1) { sigsuspend(&oset); //sigsuspend()不是下面几句的简单封装 #if 0 //类似于sigsuspend(),但是不够原子,无法实现功能。 sigset_t tmpset; sigprocmask(SIG_SETMASK, &oset, &tmpset); //sigprocmask和pause之间就把信号响应了,再用信号去打断pause就不可能了 pause();//等待信号的到来 sigprocmask(SIG_SETMASK, &tmpset, NULL); #endif } exit(0); }
- 运行接收模块,通过ps axf查看进程的pid
信号发送模块:
#include <stdlib.h> #include <stdio.h> #include <signal.h> #include <sys/types.h> //int sigqueue(pid_t pid, int sig, const union sigval value); char *buf = "Hello world"; int main(int argc, char *argv[]) { pid_t pid; union sigval sendval; if(argc < 2) { fprintf(stderr, "Usage.....\n"); exit(1); } pid = atoi(argv[1]); sendval.sival_int = 100; if(sigqueue(pid, SIGUSR2, sendval) < 0) { perror("sigqueue()"); exit(1); } exit(0); }
运行结果: