linux信号

信号

简单,不能携带大量信息,特定的条件产生。

收到信号后,不管执行到程序的什么位置,都要暂停运行,去处理信号,处理完毕再继续执行。与硬件中断类似——异步模式。但信号是软件层面上实现的中断,早期常被称为“软中断”。

由于信号是通过软件方法实现,其实现手段导致信号有很强的延时性。但对于用户来说,这个延迟时间非常短,不易察觉。

信号编号

信号4要素:

编号、名称、对应事件、默认处理动作。

信号使用之前,应先确定其4要素,而后再用.

[root@lc133 ~]# kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
编号信号对应事件默认动作
1SIGHUP用户退出 shell 时,由该 shell 启动的所有进程将收到这个信号终止进程
2SIGINT当用户按下了 <Ctrl+C> 组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号终止进程
3SIGQUIT用户按下 <ctrl+\> 组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信号终止进程
4SIGILLCPU 检测到某进程执行了非法指令终止进程并产生 core 文件
5SIGTRAP该信号由断点指令或其他 trap 指令产生终止进程并产生 core 文件
6SIGABRT调用 abort 函数时产生该信号终止进程并产生 core 文件
7SIGBUS非法访问内存地址,包括内存对齐出错终止进程并产生 core 文件
8SIGFPE在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为 0 等所有的算法错误终止进程并产生 core 文件
9SIGKILL无条件终止进程。本信号不能被忽略,处理和阻塞终止进程,可以杀死任何进程
10SIGUSE1用户定义的信号。即程序员可以在程序中定义并使用该信号终止进程
11SIGSEGV指示进程进行了无效内存访问 (段错误)终止进程并产生 core 文件
12SIGUSR2另外一个用户自定义信号,程序员可以在程序中定义并使用该信号终止进程
13SIGPIPEBroken pipe 向一个没有读端的管道写数据终止进程
14SIGALRM定时器超时,超时的时间 由系统调用 alarm 设置终止进程
15SIGTERM程序结束信号,与 SIGKILL 不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行 shell 命令 Kill 时,缺省产生这个信号终止进程
16SIGSTKFLTLinux 早期版本出现的信号,现仍保留向后兼容终止进程
17SIGCHLD子进程结束时,父进程会收到这个信号忽略这个信号
18SIGCONT如果进程已停止,则使其继续运行继续 / 忽略
19SIGSTOP停止进程的执行。信号不能被忽略,处理和阻塞为终止进程
20SIGTSTP停止终端交互进程的运行。按下 <ctrl+z> 组合键时发出这个信号暂停进程
21SIGTTIN后台进程读终端控制台暂停进程
22SIGTTOU该信号类似于 SIGTTIN,在后台进程要向终端输出数据时发生暂停进程
23SIGURG套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达忽略该信号
24SIGXCPU进程执行时间超过了分配给该进程的 CPU 时间 ,系统产生该信号并发送给该进程终止进程
25SIGXFSZ超过文件的最大长度设置终止进程
26SIGVTALRM虚拟时钟超时时产生该信号。类似于 SIGALRM,但是该信号只计算该进程占用 CPU 的使用时间终止进程
27SGIPROF类似于 SIGVTALRM,它不公包括该进程占用 CPU 时间还包括执行系统调用时间终止进程
28SIGWINCH窗口变化大小时发出忽略该信号
29SIGIO此信号向进程指示发出了一个异步 IO 事件忽略该信号
30SIGPWR关机终止进程
31SIGSYS无效的系统调用终止进程并产生 core 文件
34~64SIGRTMIN ~ SIGRTMAXLINUX 的实时信号,它们没有固定的含义(可以由用户自定义)终止进程

利用man手册查看signal

[root@lc133 ~]# man 7 signal

       信号         值      动作   说明
       ─────────────────────────────────────────────────────────────────────
       SIGHUP        1       A     在控制终端上是挂起信号, 或者控制进程结束
       SIGINT        2       A     从键盘输入的中断
       SIGQUIT       3       C     从键盘输入的退出
       SIGILL        4       C     无效硬件指令
       SIGABRT       6       C     非正常终止, 可能来自 abort(3)
       SIGFPE        8       C     浮点运算例外
       SIGKILL       9      AEF    杀死进程信号
       SIGSEGV      11       C     无效的内存引用
       SIGPIPE      13       A     管道中止: 写入无人读取的管道
       SIGALRM      14       A     来自 alarm(2) 的超时信号
       SIGTERM      15       A     终止信号
       SIGUSR1   30,10,16    A     用户定义的信号 1
       SIGUSR2   31,12,17    A     用户定义的信号 2
       SIGCHLD   20,17,18    B     子进程结束或停止
       SIGCONT   19,18,25          继续停止的进程
       SIGSTOP   17,19,23   DEF    停止进程
       SIGTSTP   18,20,24    D     终端上发出的停止信号
       SIGTTIN   21,21,26    D     后台进程试图从控制终端(tty)输入
       SIGTTOU   22,22,27    D     后台进程试图在控制终端(tty)输出

       信号           值      动作   说明
       ───────────────────────────────────────────────────────────────────
       SIGBUS      10,7,10     C     总线错误 (不正确的内存访问)
       SIGPOLL                 A     I/O就绪事件 (Sys V). 等同于SIGIO
       SIGPROF     27,27,29    A     系统资源定时器(Profiling timer)超时
       SIGSYS      12,-,12     C     用错误参数调用系统例程 (SVID)
       SIGTRAP        5        C     跟踪/断点自陷
       SIGURG      16,23,21    B     套接口上出现 urgent 情况 (4.2 BSD)
       SIGVTALRM   26,26,28    A     虚拟超时时钟 (4.2 BSD)
       SIGXCPU     24,24,30    C     超过了CPU时间限制 (4.2 BSD)
       SIGXFSZ     25,25,31    C     超过了文件大小限制 (4.2 BSD)

       信号           值      动作   说明
       ──────────────────────────────────────────────────────────────
       SIGIOT         6        C     IOT 自陷. 等同于 SIGABRT
       SIGEMT       7,-,7
       SIGSTKFLT    -,16,-     A     协处理器堆栈错误
       SIGIO       23,29,22    A     I/O 有效信号 (4.2 BSD)

       SIGCLD       -,-,18           等同于 SIGCHLD
       SIGPWR      29,30,19    A     电源无效 (System V)
       SIGINFO      29,-,-           等同于 SIGPWR
       SIGLOST      -,-,-      A     文件锁丢失
       SIGWINCH    28,28,20    B     窗口大小调整信号 (4.3 BSD, Sun)
       SIGUNUSED    -,31,-     A     未使用的信号 (将成为 SIGSYS)


默认的处理动作

       "动作(Action)"栏 的 字母 有 下列 含义:

       A      缺省动作是结束进程.

       B      缺省动作是忽略这个信号.

       C      缺省动作是结束进程, 并且核心转储.

       D      缺省动作是停止进程.

       E      信号不能被捕获.

       F      信号不能被忽略.

       (译注: 这里 "结束" 指 进程 终止 并 释放资源, "停止" 指 进程 停止 运行, 但是 资源 没有 释放, 有可能 继续 运行.)

9号信号和19号信号不能被 捕捉, 阻塞, 和 忽略

9号信号: 无条件杀死进程
19号信号: 无条件暂停进程

信号的状态

Linux 中的信号有三种状态,分别为:产生,未决,递达

  • 产生信号
  1. 按键产生,如:Ctrl+c、Ctrl+z、Ctrl+\
  2. 系统调用产生,如:kill、raise、abort
  3. 软件条件产生,如:定时器 alarm
  4. 硬件异常产生,如:非法访问内存(段错误)、除 0(浮点数例外)、内存对齐出错(总线错误)
  5. 命令产生,如:kill 命令
  • 递达:递送并且到达进程。

  • 未决:产生和递达之间的状态。主要由于阻塞(屏蔽)导致该状态。

信号处理方式: 执行默认处理动作、忽略、捕捉(自定义)

阻塞与屏蔽信号

阻塞信号集(信号屏蔽字)(本质:位图): 将某些信号加入集合,对他们设置屏蔽,当屏蔽 x 信号后,再收到该信号,该信号的处理将推后(解除屏蔽后)
未决信号集(本质:位图):
1. 信号产生,未决信号集中描述该信号的位立刻翻转为 1,表信号处于未决状态。当信号被处理对应位翻转回为 0。这一时刻往往非常短暂。
2. 信号产生后由于某些原因(主要是阻塞)不能抵达。这类信号的集合称之为未决信号集。在屏蔽解除前,信号一直处于未决状态。

信号的相关函数

kill

kill命令和kill函数 给指定进程发送指定信号,

	int kill(pid_t pid, int signum)

	参数:
		pid: 	> 0:发送信号给指定进程

			= 0:发送信号给跟调用kill函数的那个进程处于同一进程组的进程。

			< -1: 取绝对值,发送信号给该绝对值所对应的进程组的所有组员。

			= -1:发送信号给,有权限发送的所有进程。

		signum:待发送的信号

	返回值:
		成功: 0

		失败: -1 errno

父进程发送SIGKILL信号杀死子进程。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>

int main(int argc , char *argv[])
{
        pid_t pid;
        int i,temp;
        int status;

        for(i = 0; i < 2; i++){
                pid = fork();
                if(pid == 0){ break;}
                if(pid < -1){perror("fork error");exit(1);}
                if(i == 1){temp = pid;}
        }
        if(i == 0){ sleep(1);printf("我是第%d个子进程。pid=%d\n",i+1,getpid()); }
        if(i == 1){ sleep(2);printf("我是第%d个子进程。pid=%d\n",i+1,getpid());sleep(5);}
        if(i == 2){
                sleep(3);
                printf("我是父进程,我会杀死第%d个子进程,它的pid=%d。\n",i,temp);
                int ret = kill(temp,SIGKILL);
                for(i = 0; i < 2; i++){
                        wait(&status);
                        if(WIFEXITED(status)){printf("第%d子进程正常退出,状态码是:%d\n",i+1,WEXITSTATUS(status));}
                        if(WIFSIGNALED(status)){printf("第%d子进程被信号终止,信号是:%d\n " ,i+1,WTERMSIG(status));}
                }
        }
        return 0;
}

raise

给当前进程发送指定的信号

int raise(int sig);	// 参数就是要给当前进程发送的信号

abort

给当前进程发送一个固定信号 (SIGABRT)

给当前进程发送一个固定信号 (SIGABRT)

定时器

alarm

使用自然计时法。定时发送SIGALRM给当前进程。进程收到该信号,默认动作终止。

定时,与进程状态无关(自然定时法)!就绪、运行、挂起(阻塞、暂停)、终止、僵尸…无论进程处于何种状态,alarm 都计时。

	unsigned int alarm(unsigned int seconds);

		seconds:定时秒数

		返回值:上次定时剩余时间。

			无错误现象。

		alarm(0); 取消闹钟。
		
	time 命令 : 查看程序执行时间。   实际时间 = 用户时间 + 内核时间 + 等待时间。  --》 优化瓶颈 IO(文件 IO 操作需要进行用户区到内核区的切换会消耗时间)		

检测一下当前计算机 1s 钟之内能数多少个数

[lc@lc133 alarm]$ cat alarm.c
#include <stdio.h>
#include <unistd.h>

int main(int argc , char *argv[])
{
        int i = 0;
        alarm(1);

        while(1)
        {
                printf("%d\t",i);
                i++;
        }
}
[lc@lc133 alarm]$ time ./a.out   #查看程序运行时间
  ...
 28      642629  642630  642631闹钟

real    0m1.001s   # 实际数数用的总时间
user    0m0.016s	# 用户区代码使用的时间
sys     0m0.328s	# 内核区使用的时间

setitimer

设置定时器(闹钟)。 可代替 alarm 函数。精度微秒 us,可以实现周期定时。每触发一次定时器就会发射出一个信号。

	int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

	参数:
		which:	ITIMER_REAL: 采用自然计时。 ——> SIGALRM
自然计时法:自然计时法时间=用户时间 + 内核时间 + 等待时间。

			ITIMER_VIRTUAL: 采用用户空间计时  ---> SIGVTALRM

			ITIMER_PROF: 采用内核+用户空间计时 ---> SIGPROF
		
		new_value:定时秒数,给定时器设置的定时信息,传入参数

           struct itimerval {
               struct timeval it_interval; /* next value    时间间隔*/
               struct timeval it_value;    /* current value 	第一次触发定时器的时长*/
           };

           struct timeval {
               time_t      tv_sec;         /* seconds    秒*/
               suseconds_t tv_usec;        /* microseconds   微秒*/
           };
		old_value:传出参数,上次定时剩余时间,如果不需要这个信息,指定为 NULL。

setitimer定时任务

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>


void func(int signo)
{
        printf("现在时间戳是:%d,捕捉到信号:%s:%d\n",time(NULL),strsignal(signo),signo);
}


int main(int argc, char *argv[])
{
        printf("setitimer函数是一个定时器,现在的时间戳是:%d\n",time(NULL));

        struct itimerval it, oldit;

        signal(SIGALRM,func);  //信号捕捉,处理函数。

        it.it_interval.tv_sec = 3;		//时间间隔3秒
        it.it_interval.tv_usec = 0;		//微秒


        it.it_value.tv_sec = 1;			//第一次打印时间,秒
        it.it_value.tv_usec = 0;		//微秒

        int ret = setitimer(ITIMER_REAL,&it,&oldit);  //可以没有oldit设置为NULL
        if(ret == -1){perror("setitimer error");exit(1);}
        while(1);
        return 0;
}
[lc@lc133 setitimer]$ ./a.out
setitimer函数是一个定时器,现在的时间戳是:1672972922
现在时间戳是:1672972923,捕捉到信号:Alarm clock:14  //第一次打印时间相差1秒
现在时间戳是:1672972926,捕捉到信号:Alarm clock:14  //后面时间间隔3秒
现在时间戳是:1672972929,捕捉到信号:Alarm clock:14
现在时间戳是:1672972932,捕捉到信号:Alarm clock:14

信号集

pcb中两个重要的信号集(位图),阻塞信号集(信号屏蔽字mask)和未决信号集(pending)。

  • 阻塞是指阻止信号被处理,但不阻止信号的产生
  • 未决是信号的一种状态,指的是从信号的产生到信号被处理前的这一段时间。

不能直接对两个信号集直接操作,可以借助信号集函数对它们进行修改。

阻塞信号集和未决信号集在内核中的结构是相同的,它们都是一个整形数组 (被封装过的), 一共 128 字节 (int [32] == 1024 bit),1024 个标志位,其中前 31 个标志位,每一个都对应一个 Linux 中的标准信号,通过标志位的值来标记当前信号在信号集中的状态。

在阻塞信号集中
	默认情况下没有信号是被阻塞的,因此信号对应的标志位的值为 0
	如果设置为了阻塞状态,这个信号对应的标志位 被设置为 1

在未决信号集中,描述信号是否处于未决状态
	如果这个信号被阻塞了,不能抵达(不能处理),这个信号对应的标志位被设置为 1
	如果这个信号的阻塞被解除了,未决信号集中的这个信号马上就被处理了,这个信号对应的标志位值变为 0
	如果这个信号没有阻塞,信号产生之后直接被处理,就不会在未决信号集中做任何记录

信号集函数

 #include <signal.h>
 		sigset_t *set 自定义信号集,本质是位图

       int sigemptyset(sigset_t *set);    清空信号集,将set集合中所有的标志位设置为0 返回值:成功:0;失败:-1

       int sigfillset(sigset_t *set);     将set集合中所有的标志位设置为1	返回值:成功:0;失败:-1

       int sigaddset(sigset_t *set, int signum);	将set集合中某一个信号(signum)对应的标志位设置为1	返回值:成功:0;失败:-1

       int sigdelset(sigset_t *set, int signum);	将set集合中某一个信号(signum)对应的标志位设置为0	返回值:成功:0;失败:-1

       int sigismember(const sigset_t *set, int signum);	判断某个信号在集合中对应的标志位到底是0还是1, 如果是0返回0, 如果是1返回1

设置信号屏蔽字和解除屏蔽:sigprocmask

	int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

		how:	SIG_BLOCK:	设置阻塞

				SIG_UNBLOCK:	取消阻塞

				SIG_SETMASK:	用自定义set替换mask。

		set:	自定义set

		oldset:旧有的 mask。

查看未决信号集:sigpending

	int sigpending(sigset_t *set);

		set: 传出的 未决信号集。

其中9号和19号信号比较特殊,只能执行默认动作,不能忽略捕捉,不能设置阻塞。

#include <stdio.h>
#include <signal.h>
int main(int argc, char *argv[])
{
        sigset_t set, oldset, pet;
        sigemptyset(&set); //清空set集合,信号1-31对应第0-30位

#if 0
        sigaddset(&set,SIGINT); //将某一信号位置1,信号2
        sigprocmask(SIG_BLOCK,&set,&oldset);//将set标记位为1的信号屏蔽,信号2
#endif
#if 1

        sigfillset(&set);  //将信号位标志全部置1
        sigprocmask(SIG_BLOCK,&set,&oldset);//将set标记位为1的信号屏蔽,全部信号
#endif
        while(1){
                sigpending(&pet);       //获取未决信号集
                int i = 1;
                for(i; i<32; i++)
                {
                        printf("%d",sigismember(&pet,i));//打印未决集合中对应的标志位

                }
                printf("\n");
                sleep(1);
        }
        return 0;
}
#屏蔽信号2时
···
0000000000000000000000000000000
^C0100000000000000000000000000000  #传入信号2,未决信号集对应标志位为1,标识信号2被阻塞不能处理
0100000000000000000000000000000
0100000000000000000000000000000
^\退出(吐核)							#传入Ctrl+\信号时,信号正常抵达

#屏蔽全部信号时利用kill命令传入信号,2,3,5,10,31,9(9号和19号信号比较特殊)
···
^C0100000000000000000000000000000
^\0110000000000000000000000000000
0110100001000000000000000000001
已杀死

信号捕捉

signal


       #include <signal.h>

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);
参数:
	signum :待捕捉信号
	handler:捕捉信号后的操纵函数,这是一个函数指针,函数原型

信号捕捉特性:

1. 捕捉函数执行期间,信号屏蔽字 由 mask --> mask和sa_mask以及被捕捉信号的并集 , 捕捉函数执行结束。 恢复回mask

2. 捕捉函数执行期间,本信号自动被屏蔽(sa_flgs = 0).

3. 捕捉函数执行期间,被屏蔽信号多次发送,解除屏蔽后只处理一次!(后 32 个实时信号支持排队)

捕捉SIGINT信号

#include <stdio.h>
#include <signal.h>
#include <string.h>

void func(int signo)
{
        printf("捕捉到信号:%s,%d\n",strsignal(signo),signo);
}
int main(int argc, char *argv[])
{
        signal(SIGINT,func);
        while(1);
        return 0;
}
^C捕捉到信号:Interrupt,2
^C捕捉到信号:Interrupt,2
^C捕捉到信号:Interrupt,2
^C捕捉到信号:Interrupt,2

sigaction

       #include <signal.h>

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

	signum: 要捕捉的信号

	act: 捕捉到信号之后的处理动作

	oldact: 上一次调用该函数进行信号捕捉设置的信号处理动作,该参数一般指定为 NULL
	
返回值:函数调用成功返回 0,失败返回 - 1

该函数的参数是一个结构体类型:
           struct sigaction {
               void     (*sa_handler)(int); //指向一个函数(回调函数)
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;  // 初始化为空即可, 处理函数执行期间不屏蔽任何信号
               int        sa_flags;  //0
               void     (*sa_restorer)(void);
           };
结构体成员:           
sa_handler:指定信号捕捉后的处理函数名(即注册函数)。也可赋值为 SIG_IGN 表忽略 或 SIG_DFL 表执行默
认动作

sa_sigaction:当 sa_flags 被指定为 SA_SIGINFO 标志时,使用该信号处理程序。(很少使用)

② sa_mask: 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽
生效,是临时性设置。

sa_flags:使用哪个函数指针指向的函数处理捕捉到的信号
	0:使用 sa_handler (一般情况下使用这个)
	SA_SIGINFO:使用 sa_sigaction (使用信号传递数据 == 进程间通信)
	
sa_restorer:该元素是过时的,不应该使用,

捕捉信号

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>

#include <string.h>
void func(int signo)
{

        printf("捕捉到的信号%s:%d\n睡4秒\n",strsignal(signo),signo);
        sleep(4);
        printf("睡眠完毕\n");
}

int main(int argc,char *argv[])
{

        struct sigaction act;


        sigset_t set;
        sigemptyset(&set);
        sigaddset(&set,SIGTSTP);     //设置默认的信号屏蔽字mask,屏蔽ctrl+z
        sigprocmask(SIG_BLOCK,&set,NULL);


        act.sa_handler = func;
        act.sa_flags = 0;
        sigemptyset(&act.sa_mask);
        sigaddset(&act.sa_mask,SIGQUIT);//设置信号捕捉期间信号屏蔽字sa_mask,屏蔽信号ctrl + \
        //在实际信号捕捉执行期间,会屏蔽捕捉的信号和mask,sa_mask信号屏蔽字的并集

        sigaction(SIGINT,&act,NULL);  //捕捉信号ctrl + c
        printf("------sleep(10)--------\n");
        sleep(10);

        while(1);

        return 0;
}

[lc@lc133 sigaction]$ ./a.out
------sleep(10)--------
^C捕捉到的信号Interrupt:2
睡4秒
^C^C^C^C^C^C^C^Z^Z^Z^Z^Z^\^\睡眠完毕   #/在实际信号捕捉执行期间,会屏蔽捕捉的信号和mask,sa_mask信号屏蔽字的并集,SIGINT,SIGTSTP,SIGQUIT都被屏蔽
捕捉到的信号Interrupt:2					#信号捕捉程序执行期间多次收到同信号,只会处理一次,
睡4秒
^\^\^\睡眠完毕
退出(吐核)								#SIGTSTP被屏蔽,所以处理SIGQUIT信号

借助信号捕捉回收子进程

SIGCHLD信号 ,子进程状态发生变化时会发生产生SIGCHLD信号,可以被捕捉

#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>


void func(int signo)
{
        int status,ii=1;
        while( wait(&status) != -1){    //循环回收多个子进程,就算全部子进程同时死掉由于sigchld信号的阻塞
                                        //会只处理一次sigchld信号,但也会回收全部子进程

                printf("我已经回收了%d个子进程。\n",ii);
                ii++;
                if (WIFEXITED(status)) {printf("exited, status=%d\n", WEXITSTATUS(status));}

                sleep(1);  //模拟处理信号需要时间
        }

}

int main(int argc, char *argv[])
{

        struct sigaction act;
        int pid,i=0;
        sigset_t set;
        sigemptyset(&set);
        sigaddset(&set,SIGCHLD);
        sigprocmask(SIG_BLOCK,&set,NULL);       //设置阻塞,防止子进程在父进程开始之前结束,捕捉不到信号


        for(i;i<20;i++)
        {
                pid = fork();
                if(pid == 0){break;}
                if(i == 19 ){sleep(2);}   //父进程在子进程之后执行
        }
        if(i != 20) {printf("我是第%d个子进程,pid:%d\n",i+1,getpid());}
        sleep(2);
        if(i == 20)
        {
                act.sa_handler = func;
                sigemptyset(&act.sa_mask);
                act.sa_flags = 0;

                sigaction(SIGCHLD,&act,NULL);  //注册捕捉函数
                sigprocmask(SIG_UNBLOCK,&set,NULL);  //在注册捕捉函数后,取消阻塞

                while(1);
        }
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值