【C/C++】多进程:信号量的监听与处理函数
文章结构:
之前的多进程:父进程监听子进程状态 wait()的使用文章中,父进程为了获取子进程的SIGSTOP、SIGTERM等信号时,由于调用了wait而导致主进程一直阻塞。在实际的开发中,主进程在等待子进程状态变化时还会有其它的事情要去执行,所以需要一种异步回调机制,让主进程可以在执行其它任务的时候,又可以监听到子进程的进程状态变化时及时处理。
signal()函数就可以解决以上的问题。
signal()函数原型如下:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler); typedef定义了一类函数名叫sighandler_t,该函数返回类型为void,且只有一个int型参数。
signal()函数第一个参数signum指所要监听的进程状态的变量信号,所有可监听的信号量的定义可以从sys/signal.h头文件中去查阅。本文文章的demo中要处理的信号量有:
SIGSTOP: 发送给父进程的,表示子进程被外部命令所暂停。命令可以是
kill,也可以在top中操作。
SIGCHLD: 发送给父进程的,表示子进程被外部命令所暂停或已经执行完毕退出。这时需要父进程执行
wait函数让子进程从僵尸进程状态彻底被系统回收。
SIGWINCH: 程序窗口大小发生变化。在终端命令行下运行可执行文件时鼠标拖动一下窗口即可获得此信号。
signal()函数第二个参数是指定signum的处理函数。该函数的唯一参数将会被赋值为被监听到的信号量。在此函数中可以调用wait或其它处理逻辑。也可以赋值为系统的SIG_IGN或SIG_DFL函数,分别表示忽略和默认处理方式。但是信号量SIGKILL及SIGSTOP的处理方式是不能被忽略处理。
当自定义了信号量处理函数后,所监听的信号被捕获,则该信号会被设置为阻塞blocked,然后再执行处理函数中的逻辑,处理函数执行完毕后,信号量恢复为未阻塞状态unblocked。
signal()函数正常执行,返回值为signum的原有处理函数;否则出错返回SIG_ERR,并且可以通过errno来查看错误原因。
signal()函数在不同的Unix或Linux版本间存在较大的差异,所以一般推荐用sigaction()函数来替换。本文不涉及sigaction()的内容。
#示例代码
接下来演示signal()函数的使用。代码示例中依然用到了对标准输出流的重定向freopen,将子进程的日志输出到child_signal.txt,父进程日志输出到main_signal.txt中去。
signal.c
1 |
|
以上代码中,在main()函数一开始,就对SIGWINCH、SIGSTOP、SIGCHLD进行监听,统一注册其处理函数为handleSignal(int)。然后执行fork()生成子进程。
handleSignal(int)函数中会对监听到的信号量做出打印及输出,如果是SIGSTOP、SIGCHLD的话则会执行wait以获取子进程状态。
在主进程中,以mainProcessDoSomething()函数来表示父进程的其它工作任务,不被wait所阻塞。
编译signal.c文件,后在命令行终端下执行./a.out,然后鼠标拖动改动一个命令行终端窗口的大小,可见child_signal.txt及main_signal.txt都输出了handleSignal SIGWINCH=28的日志。
然后kill -sigstop child_pid,再恢复kill -sigcont child_pid,然后一直等待子进程运行完毕,可完整看到如下两份日志。
child_signal.txt
2015-04-19 22:31:31 Child:SelfID=4352 ParentID=4351
2015-04-19 22:31:36 Child:count=1
2015-04-19 22:31:39 PID=4352 handleSignal SIGWINCH=28
2015-04-19 22:31:39 Child:count=2
2015-04-19 22:31:39 PID=4352 handleSignal SIGWINCH=28
2015-04-19 22:31:39 Child:count=3
2015-04-19 22:31:39 PID=4352 handleSignal SIGWINCH=28
2015-04-19 22:31:39 Child:count=4
2015-04-19 22:31:54 Child:count=5
2015-04-19 22:31:59 Child:count=6 // 这里,对子进程执行了kill -sigstop命令
2015-04-19 22:32:21 PID=4352 handleSignal SIGCONT=19
2015-04-19 22:32:21 Child:count=7
2015-04-19 22:32:26 Child:count=8
... ...
... ...
2015-04-19 22:33:16 Child:count=20
2015-04-19 22:33:16 Child:SelfID=4352 exit success.main_signal.txt
2015-04-19 22:31:31 Parent:SelfID=4351 MyChildPID=4352
2015-04-19 22:31:39 PID=4351 handleSignal SIGWINCH=28
2015-04-19 22:31:39 PID=4351 handleSignal SIGWINCH=28
2015-04-19 22:31:39 PID=4351 handleSignal SIGWINCH=28
2015-04-19 22:32:04 PID=4351 handleSignal SIGCHLD=20
2015-04-19 22:32:04 pid=4351 child=4352 exitCode=4479 status=0 ifExited=0 ifSignaled=0 ifStopped=1 ifContinued=0
2015-04-19 22:32:04 PID=4352 stopped by signal 17
2015-04-19 22:33:16 PID=4351 handleSignal SIGCHLD=20
2015-04-19 22:33:16 pid=4351 child=4352 exitCode=0 status=0 ifExited=1 ifSignaled=0 ifStopped=0 ifContinued=0
2015-04-19 22:33:16 PID=4352 exited, status=0 // 父进程监听到子进程执行完毕
2015-04-19 22:33:55 Parent:SelfID=4351 exit success. // 父进程WHILE循环执行完毕 可以发现发送给子进程的SIGSTOP和运行退出对父进程来说都是SIGCHLD。而子进程可以接收到父进程wait方法中不支持的SIGCONT信号。