信号
一、信号是什么东西
信号是事件发生时通知进程的一种机制,有时也称之为软件中断。
信号的到来会打断了程序执行的正常流程。
大多数情况下,无法预测信号到达的精确时间。
一个(具有合适权限的)进程能够向另一进程发送信号。
信号的这一用法可作为一种同步技术,甚至是进程间通信( IPC )的原始形式。
进程也可以向自身发送信一号。
发往进程的诸多信号,通常都是源于内核,引发内核为进程产生信号的各类事件如下:
1、硬件发生异常,即硬件检测到一个错误条件并通知内核,随即再由内核发送相应信号给相关进程。
硬件异常的例子包括执行一条异常的机器语言指令,诸如,被 0 除,或者引用了无法访问的内存区域。
2、用户键入了能够产生信号的终端特殊字符。比如:中断字符,通常是 Ctrl+C 。
3、发生了软件事件。
例如,针对文件描述符的输出变为有效,调整了终端窗口大小,定时器到期,进程执行的 CPU 时间超限,
或者该进程的某个子进程退出。
查看信号命令:kill -l
1-31:标准信号
32-64:实时信号
标准信号的简要说明:
- SIGHUP
本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。登录Linux时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程,因此前台进程组和后台有终端输出的进程就会中止。不过可以捕获这个信号,比如wget能捕获SIGHUP信号,并忽略它,这样就算退出了Linux登录,wget也能继续下载。
此外,对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。 - SIGINT
程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl+C)时发出,用于通知前台进程组终止进程。 - SIGQUIT
和SIGINT类似, 但由QUIT字符(通常是Ctrl-)让进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。 - SIGILL
执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。 - SIGTRAP
由断点指令或其它trap指令产生. 由debugger使用。 - SIGABRT
调用abort函数生成的信号。 - SIGBUS
非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。 - SIGFPE
在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。 - SIGKILL
用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。 - SIGUSR1
留给用户使用 - SIGSEGV
试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据. - SIGUSR2
留给用户使用 - SIGPIPE
管道破裂。这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止。 - SIGALRM
时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号. - SIGTERM
程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,shell命令kill缺省产生这个信号。如果进程终止不了,我们才会尝试SIGKILL。 - SIGCHLD
子进程结束时, 父进程会收到这个信号。
如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程来接管)。 - SIGCONT
让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符 - SIGSTOP
停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略. - SIGTSTP
停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号 - SIGTTIN
当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行. - SIGTTOU
类似于SIGTTIN, 但在写终端(或修改终端模式)时收到. - SIGURG
有"紧急"数据或out-of-band数据到达socket时产生. - SIGXCPU
超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变。 - SIGXFSZ
当进程企图扩大文件以至于超过文件大小资源限制。 - SIGVTALRM
虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间. - SIGPROF
类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间. - SIGWINCH
窗口大小改变时发出. - SIGIO
文件描述符准备就绪, 可以开始进行输入/输出操作. - SIGPWR
Power failure - SIGSYS
非法的系统调用。
二、标准信号相关函数
1、改变信号处置:signal()
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int sig, sighandler_t handler);
handler 可以是一个函数指针,也可以指定为如下值:
SIG_DFL 将信号重置为默认值。
SIG_IGN 忽略信号。
返回值:这次改变之前的处理信号的行为。
用程序终止信号SIGINT来举例子:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
void (*oldHandler)(int);
oldHandler = signal(SIGINT, SIG_IGN); //IGN忽略中断信号,则按ctrl+c不会罗软中断
if (oldHandler == SIG_ERR) perror("signal");
for (int i = 0; i < 10; i++) {
sleep(1);
printf("*"); //按ctrl+c不管用,中断不了,一直到10s后。
fflush(stdout);
}
// oldHandler = signal(SIGINT, oldHandler);
oldHandler = signal(SIGINT, SIG_DFL);
if (oldHandler == SIG_ERR) perror("signal");
for (int i = 0; i < 10; i++) {
sleep(1);
printf("*"); //ctrl+c管用了
fflush(stdout);
}
return 0;
}
信号处理简介
调用信号处理器程序,可能随时会打断主程序流程。
内核代表进程来调用处理器程序,当处理器返回时,主程序会在处理器打断的位置恢复执行。
另外:信号会打断阻塞,
所以当我们调用会阻塞的函数的时候,判断错误的时候,应该排除收到信号打断阻塞的情况。
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(void) {
int fd = -1;
int ret = 0;
fd = open("/dev/stdin", O_RDONLY);
if (fd < 0) {
perror("open stdin is fail");
exit(-1);
}
char buf[1024] = {0};
while(1){
ret = read(fd, buf, 1024); //会阻塞
if (ret < 0) {
//出错,也有假错,比如这里遇到信号,所以这种可那个,健壮的程序,应该考虑到
if(errno == EINTR){
continue;
}
perror("read error");
return -1;
}
printf("ret:%d-%s", ret, buf);
}
close(fd);
return 0;
}
2、发送信号:kill()
一个进程可以向另一个进程发送信号(必须要有权限)。
#include <signal.h>
#include <sys/types.h>
int kill(pid_t pid, int sig);
成功返回0, 失败-1
pid > 0发送为指定进程
pid = 0发送给当前进程组里所有进程包括自己
pid < -1 发送给进程组id = pid的绝对值的进程组下的每个进程,效果等同killpg(pid_t pgrp, int sig);
pid = -1发送给所有进程(必须有权限)除了1号进程(初始化时)
sig = 0为空信号用来检测进程是否存在,不会用的,不太正确
进程是否存在,不正确,下面的如30号进程,杀死了,又创建的进程具有可能仍为30号,
int is_p_alive(pid_t pid) {
int kill_rc = kill(pid, 0); //发送0号信号,测试是否存活
if (kill_rc == ESRCH) { //进程不存在,不可靠,Unix/Linux系统经过一段时间会重复使用进程ID,
return false; //一个所给定进程ID存在并不一定是你想要找的进程,或许它是一个新的进程,
} //你需要的进程早已经死亡消失在内存中!
return true;
}
特权级进程可以向任何进程发送信号,非特权级必须满足如下图条件。
进程的实际用户ID:
标识我们实际上是谁,是当前登录的用户ID.
进程的有效用户ID:
决定的是文件的访问权.通常有效用户ID就是实际用户ID.
进程的设置-用户-ID,
是一个特殊标志,当该标志设置时,执行该文件时的有效用户ID就是文件的所有者ID。
比如,当一个文件的所有者是root当你以另一个用户登录时,如果没有设置-用户-ID,执行该文件时的有效ID和实际ID就是登录用户的ID,是无权限操作这个文件的。但如果设置了设置-用户-ID,实际用户ID是登录用户ID,而有效用户ID是root用户的ID。
看个例子就更清楚了:
//默认情况下
zhonghao执行cat,系统创建一个cat进程,进程的属主属组取程序发起者,也就是zhonghao
cat进程访问/etc/shadow,由于进程属主属组是zhonghao,与/etc/shadow的属组属主都不匹配,所以被拒绝访问.
给cat设置SUID之后
zhonghao执行cat.系统创建一个cat进程,进程的属主取cat的属主,属组取程序发起者,就是root
cat进程访问/etc/shadow,由于进程属主是root,与/etc/shadow的属主匹配,所以被允许访问.
which cat
/usr/bin/cat
chmod u+s /usr/bin/cat //s是特殊权限,具有设置到用户ID 作用
su - zhonghao
cat /etc/shadow //可以看了
3、向自己发送信号:raise()
#include <signal.h>
int raise(int sig);
//成功返回0,失败返回非0
//单线程程序相当于
kill(getpid(), sig);
//多线程相当于
pthread_kill(pthread_self(), sig); //当前线程ID号,信号
4、向进程组发送信号killpg
#include <signal.h>
int killpg(pid_t pgrp, int sig);
//成功返回0,失败-1
//相当于kill函数中pid<-1的模式
kill(-pgrp, sig);
5、信号描述
  ;每个信号都有一段与之相对应的描述,可以用strsignal() 函数获取。
#include <string.h>
char *strsignal(int sig);
//也可以直接打印到错误输出
#include <signal.h>
void psignal(int sig, const char *msg);
看例子:
#include <signal.h>
#include <stdio.h>
#include <string.h>
int main(){
char *tmp = strsignal(SIGINT);
printf("%s\n", tmp); //Interrupt,中断的意思
psignal(SIGINT,"sigerr"); //sigerr:Interrupt
}
6、信号集
许多信号相关的系统调用都需要能表示一组不同的信号。
多个信号可使用一个称之为信号集的数据结构来表示,数据类型为 sigset_t。
sigemptyset()
初始化一个未包含任何成员的信号集。
sigfillset()
初始化一个信号集,包含所有信号。
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
//成功返回0,失败-1
必须使用上述2个函数初始化信号集,因为C语言不会对自动变量进行初始化。
添加或移除单个信号
#include <signal.h>
int sigaddset(sigset_t *set, int sig);
int sigdelset(sigset_t *set, int sig);
//成功返回0,失败-1
测试信号 sig 是否为 set的成员
#include <signal.h>
int sigismember(const sigset_t *set, int sig);
//1表示是,0不是
7、信号掩码(阻塞信号传递)
也可叫信号屏蔽。内核会为每个进程维护一个信号掩码,即一组信号,并将阻塞其针对该进程传递。
在多线程环境中,每个线程都可以使用pthread_sigmask()函数来独立检查和修改其信号。
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
//Returns 0 on success, or –1 on error
sigprocmask() 既可以修改信号掩码,又可获取现有的掩码,或两者兼具。
how:参数指定了该函数信号掩码带来的变化:
SIG_BLOCK 将set 指向信号集并集到当前信号掩码中。
SIG_UNBLOCK 将set指向的信号集中的信号从信号掩码中移除。
SIG_SETMASK 将set指向的信号集设置为信号掩码。
如果 oldset 参数不为NULL,则其指向sigset_t 结构缓冲区,用于返回之前的信号掩码。
如果只是想获取信号掩码, set 参数设为NULL即可,这时将忽略how 参数。
上面讲的屏蔽实质上是一种阻塞,如果解除了对某个等待信号的阻塞,会立刻将该信号传递给进程。
下面代码时暂时阻止 SIGINT 信号的传递。
sigset_t blockSet, prevMask;
sigemptyset(&blockSet); //初始化信号集,初始为NULL
sigaddset(&blockSet, SIGINT); //增加信号SIGINT
if (sigprocmask(SIG_BLOCK, &blockSet, &prevMask) == -1) //并集到prevMask集中
perror("sigprocmask1");
if (sigprocmask(SIG_SETMASK, &prevMask, NULL) == -1)
perror("sigprocmask2");
SIGKILL 和 SIGSTOP 信号是不允许阻塞的,也就是下面的代码会阻塞除这2个信号以外的任何信号。
sigfillset(&blockSet);
if (sigprocmask(SIG_BLOCK, &blockSet, NULL) == -1)
perror("sigprocmask");
8、获取等待中的信号集
如果某进程接收了一个正在阻塞的信号,那么会将该信号添加到进程的等待信号集中。之后如果解除了对该信号的阻塞,就会把该信号传递给此进程(就算在阻塞期间发生了N次,解除时只会传递1次,而实时信号可以排队)。
sigpending()
系统调用返回进程处于等待状态的信号集,并存入set 指向的sigset_t 结构中。
#include <signal.h>
int sigpending(sigset_t *set);
//Returns 0 on success, or –1 on error
写一下例子:我阻塞SIGINT信号不让其进入当前进程中,再我们又按 ctrl+c看看SIGINT是否在当前等待信号集中,结果是肯定的。
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
int main(){
//定义三个信号集的变量
sigset_t blockset,prvmask,pendingset;
//blockset初始化:空
sigemptyset(&blockset);
//屏弊SIGINT
sigaddset(&blockset,SIGINT); //给空集加SIGINT信号(linux中的ctrl+c软中断信号)
if(sigprocmask(SIG_BLOCK,&blockset,&prvmask)==-1){ //SIG_BLOCK将blockset集并到掩码集中,即阻塞其进入进程中,最后保存到prvmask集中
perror("sigprocmask出错,没有掩码了!");
}
int i=0;
//下面代码5秒钟中打出五个数,由于上面屏蔽了SIGINT信号,导致在打出(5秒之内—)时,按ctrl+c不管用,中断不了
for(i=0;i<5;i++){
sleep(1);
printf("i=%d ",i);
fflush(stdout);
}
//试图把等待着的信号集拿出来,判断一下SIGINT在不在里边(按一下ctrl+c,应在里面)
if(sigpending(&pendingset)== -1){
perror("sigpending!error!");
}
if(sigismember(&pendingset,SIGINT)){
printf("SIGINT 在pendingset里面!\n");
}else{
printf("SIGINT 不在pendingset里面!\n");
}
}
9、改变信号处置另一个函数:sigaction()
除signal() 之外,sigaction() 系统调用是设置信号处置的另一选择。
用法复杂一点,但是功能强大,且可移植性强。
#include <signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
//Returns 0 on success, or –1 on error
sig为想要获取或改变的信号编号,除去 SIGKILL和SIGSTOP。
act是指向信号新处置的数据结构,
oldact用来返回之前的信号处置结构,都可以设置为 NULL。
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此参数和signal()的参数handler相同,代表新的信号处理函数。
sa_sigaction 则是另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。
sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集里的信号屏蔽掉
sa_flags 用来设置信号处理的其他相关操作,下列的数值可用:
◆ SA_RESTART:使被信号打断的系统调用自动重新发起。
◆ SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。
◆ SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。
◆ SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
◆ SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
◆ SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数。
看一个例子:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void show_handler(int sig)
{
printf("I got signal %d\n", sig);
int i;
for(i = 0; i < 5; i++)
{
printf("i = %d\n", i);
sleep(1);
}
}
int main(void)
{
int i = 0;
struct sigaction act, oldact;
act.sa_handler = show_handler;
sigaddset(&act.sa_mask, SIGQUIT);
sigaddset(&act.sa_mask, SIGTERM);
act.sa_flags = SA_RESETHAND | SA_NODEFER;
sigaction(SIGINT, &act, &oldact);
while(1)
{
sleep(1);
printf("sleeping %d\n", i);
i++;
}
}
三、实时信号
实时信号意在弥补对标准信号的诸多限制。
其优势如下:
1、实时信号的信号范围有所扩大,可应用于自定义目的,
而标准信号中可提供随意使用的只有SIGUSR1和 SIGUSR2。
2、实时信号为队列化管理,同一信号发送多次将会多次传递给进程。
3、发送信号可以伴随数据(一整形或者指针值)
4、不同实时信号同时处于等待状态时,那么率先传递较小编号的信号。
如果排队的是同一类型的信号,那么信号的传递顺序会按照发送来的的顺序传给进程。
发送实时信号,sigqueue 函数
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {
int sival_int; /* Integer value for accompanying data */
void sival_ptr; / 很少用到 */
};
//成功返回0,失败-1
一旦触及对排队信号的数量限制(ulimit -i查看和修改),sigqueue()调用会失败,errno置为EAGAIN。
其他kill()、killpg()、raise()调用也能发送实时信号,但是排不排队由具体实现决定,linux下是排队的。
处理实时信号
可以像标准信号一样,使用常规(1个参数)信号处理器来处理实时信号。
也可以用带有3个参数的信号处理器函数来处理实时信号。
一旦采用了第二种方式,第二个参数是一个siginfo_t 结构。
对于一个实时信号而言,会在siginfo_t 结构中设置如下字段:
1.si_signo,其值与传递给信号处理器函数的第一个参数相同。
2.si_code,表示信号来源,由sigqueue()发送的实时信号来说,该值是SI_QUEUE,由用户用kill命令发送的信号,该值是SI_USER。
3.si_value,为进程于 sigqueue() 带过来的额外参数,sigval union。
4.si_pid 和 si_uid,分别为信号发送进程的进程id,实际用户id。
例子
main.c 用来接收信号。可带2个参数:程序休眠时间(为了让信号排队)、信号处理器函数处理间隔时间。
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
//demo会用到的一些全局变量
static volatile int handlerSleepTime; //信号处理器的休眠间隔时间
static volatile int sigCnt; //信号处理次数的统计
static volatile int allDone; //信号队列里的信号是不是都处理完的标志
//处理信号的函数,一个参数,三个参数
static void siginfoHandler(int sig, siginfo_t *si,void *ucontext){
//如果来的信号是SIGINT SIGTERM,结束处理
if(sig == SIGINT || sig == SIGTERM){
allDone = 1;
return;
}
sigCnt++;
printf(“到达处理的信号是:%d\n”,sig);
printf(“si_signo:%d----si_code:%d (%s)-----si_pid:%d----si_uid:%d \n”, si->si_signo, si->si_code,
(si->si_code == SI_USER) ? “SI_USER” :
(si->si_code == SI_QUEUE) ? “SI_QUEUE” : “other”,si->si_pid,si->si_uid);
sleep(handlerSleepTime); //休眠是为了,一会我们体现标准信号会丢弃,实时队列管理,不会丢失
}
//执行这个函数的时候,传两个参数过来,第一个参数主程序休眠的间隔时间,信号处理器休眠间隔时间
int main(int argc, char *argv[]) {
//声明要用的局部变量
struct sigaction sa;
int sig;
sigset_t prevMask,blockMask;
printf(“要去接受处理信号的进程的id:%ld\n”,(long)getpid());
handlerSleepTime = atoi(argv[2]);
sa.sa_sigaction = siginfoHandler;
sa.sa_flags = SA_SIGINFO;
sigfillset(&sa.sa_mask);
sigfillset(&blockMask);
sigdelset(&blockMask,SIGINT);
sigdelset(&blockMask,SIGTERM);
//所有信息的处置方式都设置为sa
for(sig=1;sig<NSIG;sig++){
sigaction(sig,&sa,NULL);
}
//信号屏蔽
if(sigprocmask(SIG_SETMASK,&blockMask,&prevMask)==-1){
perror(“sigprocmask”);
}
printf(“main 开始休眠…\n”);
sleep(atoi(argv[1]));
printf(“main 休眠结束…\n”);
//接触信号屏蔽
if(sigprocmask(SIG_SETMASK,&prevMask,NULL)==-1){
perror(“sigprocmask”);
}
while(!allDone){
pause(); //等信号到达,直接alldone为1
}
printf(“main 程序终止!\n”);
return 0;
}
send.c 用来发送信号
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
union sigval sv;
if (sigqueue(atoi(argv[1]), atoi(argv[2]), sv) == -1)
perror("sigqueue");
return 0;
}
使用掩码来等待信号:sigsuspend()
对信号编程时偶尔会遇到如下情况:
1.临时阻塞一个信号,以防止在处理关键代码时被此信号打断
2.关键代码执行完后,需要暂停执行,等到有信号到达,在放行
sissuspend() 系统调用将以mask 所指向的信号集来替换信号掩码,然后挂起进程的执行,直到其捕获到信号,
并从处理器函数返回,返回后,信号掩码恢复为调用前的值。
#include <signal.h>
int sigsuspend(const sigset_t *mask);
//(Normally) returns –1 with errno set to EINTR
例子:
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define MYRTSIG (SIGRTMIN+6)
//自定义的信号处置函数
static void mysig_handler(int s){
printf(" mysig_handler run! ");
fflush(stdout);
}
int main() {
int i=0,j=0;
sigset_t set,oset,saveset;
printf(“我的进程id:%ld\n”,(long)getpid());
signal(MYRTSIG,mysig_handler);
sigemptyset(&set);
sigaddset(&set,MYRTSIG);
sigprocmask(SIG_BLOCK,&set,&saveset);
for(i=0;i<1000;i++){
for(j=0;j<10;j++){
write(1,“*”,1);
sleep(1);
}
write(1,“\n”,1);
sigsuspend(&oset);
}
sigprocmask(SIG_SETMASK,&saveset,NULL);
return 0;
}
以同步方式等待信号
同步方式等待信号会相对简单易控一点。
#include <signal.h>
int sigwaitinfo(const sigset_t *set, siginfo_t *info);
//成功返回信号编号,失败-1
int sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout);
//可以指定超时时间,如果调用超时而又没有收到信号返回-1
//并设置errno为EAGAIN
sigwaitinfo() 系统调用挂起进程的执行,直至set指向的信号集中的某一信号到达。
如果有信号处于等待状态,则立马返回。
info 如果不为NULL,则会指向经过初始化处理的 siginfo_t 结构。
sigwaitinfo() 接受信号的顺序和排队特性与信号处理器所捕获的信号相同。
标准信号不排队,实时信号按照低编号优先。
除了不需要编写信号处理器,它的速度也更快一点。
例子
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
// SIGQUIT 用
void handler(int s) { printf(“handler :%d\n”, s); }
int main(int argc, char** argv) {
printf(“PID:%ld\n”,(long)getpid());
signal(SIGQUIT, handler); // ctrl + , 此信号没BLOCK
siginfo_t info;
sigset_t mask;
// sigfillset(&mask);
sigaddset(&mask, SIGRTMIN + 1);
sigaddset(&mask, SIGRTMIN + 2);
sigprocmask(SIG_BLOCK, &mask, NULL); //设置屏蔽
int sig = -1;
while (1) {
//阻塞等待
sig = sigwaitinfo(&mask, &info);
if (sig < 0) {
if (errno == EINTR) {
// 上面 sigquit 将在这里中断
perror(“sigwaitinfo false”);
continue;
}
perror(“sigwaitinfo”);
break;
}
printf(“sig:%d , from pid:%d , code:%d , signo:%d\n”, sig, info.si_pid,
info.si_code, info.si_signo);
// ctrl+c 就结束了
if (SIGINT == sig) {
break;
}
}
return 0;
}