多进程:信号量的监听与处理函数
2016-05-27 11:28
309 查看
文章结构:
wait()的阻塞问题
signal()函数讲解
示例代码
SIGSTOP: 发送给父进程的,表示子进程被外部命令所暂停。命令可以是
SIGCHLD: 发送给父进程的,表示子进程被外部命令所暂停或已经执行完毕退出。这时需要父进程执行
SIGWINCH: 程序窗口大小发生变化。在终端命令行下运行可执行文件时鼠标拖动一下窗口即可获得此信号。
当自定义了信号量处理函数后,所监听的信号被捕获,则该信号会被设置为阻塞
以上代码中,在
在主进程中,以
编译
然后
child_signal.txt
main_signal.txt
可以发现发送给子进程的
wait()的阻塞问题
signal()函数讲解
示例代码
wait()的阻塞问题
之前的多进程:父进程监听子进程状态 wait()的使用文章中,父进程为了获取子进程的SIGSTOP、SIGTERM等信号时,由于调用了wait而导致主进程一直阻塞。在实际的开发中,主进程在等待子进程状态变化时还会有其它的事情要去执行,所以需要一种异步回调机制,让主进程可以在执行其它任务的时候,又可以监听到子进程的进程状态变化时及时处理。
signal()函数就可以解决以上的问题。
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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 | #include <stdio.h>#include <signal.h>#include <stdlib.h>#include <sys/wait.h>#include <unistd.h>#include <errno.h>#include <time.h>#include <string.h>#ifndef getSigName#define getSigName(sig) (((sig) == SIGCHLD)?"SIGCHLD":(((sig) == SIGCONT)?"SIGCONT":(((sig) == SIGTERM)?"SIGTERM":(((sig) == SIGWINCH)?"SIGWINCH":""))))#endifstatic void printTime() { time_t calendar_time = time(NULL); struct tm * tm_local = localtime(&calendar_time); char str_f_t [50]; strftime(str_f_t, sizeof(str_f_t), "%G-%m-%d %H:%M:%S", tm_local); printf("%s ", str_f_t);}static void handleSignal(int sig) { printTime(); printf("PID=%d handleSignal %s=%d\n", getpid(), getSigName(sig), sig); if (sig == SIGCHLD || sig == SIGTERM) { // 子进程被暂停或退出了(包括按逻辑执行结束及被kill) int status = 0; int childPid = waitpid(-1, &status, WUNTRACED|WCONTINUED); if (childPid == -1) { printTime(); printf("Parent w=-1, error=%s \n", strerror(errno)); } else { int ifExited, ifSignaled, ifStopped, ifContinued; ifExited = WIFEXITED(status); ifSignaled = WIFSIGNALED(status); ifStopped = WIFSTOPPED(status); ifContinued = WIFCONTINUED(status); printTime(); printf("pid=%ld child=%d exitCode=%d status=%d ifExited=%d ifSignaled=%d ifStopped=%d ifContinued=%d \n", (long)getpid(), childPid, status, _WSTATUS(childPid), ifExited, ifSignaled, ifStopped, ifContinued); printTime(); if (ifExited) { printf("PID=%ld exited, status=%d\n", (long)childPid, WEXITSTATUS(status)); } else if (ifSignaled) { printf("PID=%ld killed by signal %d\n", (long)childPid, WTERMSIG(status)); } else if (ifStopped) { printf("PID=%ld stopped by signal %d\n", (long)childPid, WSTOPSIG(status)); } else if (ifContinued) { printf("PID=%ld continued\n", (long)childPid); } } } else if (sig == SIGCONT) { // sigcont在本人的mac上调用wait是无效的 // do nothing } else if (sig == SIGWINCH) { // do nothing } else { printTime(); printf("sig=%d is not valid.\n", sig); }}static void mainProcessDoSomething(FILE* f) { int count = 0; while(1) { sleep(3); if (f != NULL){ fflush(f); } if (count ++ > 50) { break; } }}int main/*11*/ (int argc, char ** argv) { // SIGWINCH:应用程序窗口发生变化 signal(SIGWINCH, &handleSignal); // 子进程被暂停运行 signal(SIGSTOP, &handleSignal); // 子进程被恢复运行(Mac上无效..) signal(SIGCONT, &handleSignal); // SIGCHLD:子进程被暂停或退出了(包括按逻辑执行结束及被kill) signal(SIGCHLD, &handleSignal); // 不建议对SIGTERM进行设置 // signal(SIGTERM, &handleSignal); pid_t pId = fork(); if (pId == -1) { perror("fork error"); exit(EXIT_FAILURE); } else if (pId == 0) { FILE* fChild = freopen("/Users/sodino/workspace/xcode/Define/Define/child_signal.txt", "w", stdout); int myPid = getpid(); int parentPid = getppid(); printTime(); printf("Child:SelfID=%d ParentID=%d \n", myPid, parentPid); int count = 0; do{ count ++; sleep(5); printTime(); printf("Child:count=%d \n", count); fflush(fChild); if (count >= 20) { break; } }while (1); printTime(); printf("Child:SelfID=%d exit success.\n", myPid); fflush(fChild); fclose(fChild); return EXIT_SUCCESS; } else { FILE * fMain = freopen("/Users/sodino/workspace/xcode/Define/Define/main_signal.txt", "w", stdout); printTime(); printf("Parent:SelfID=%d MyChildPID=%d \n", getpid(), pId); fflush(fMain); // 继续往下执行其它任务,而不像原逻辑 会被wait()所阻塞 mainProcessDoSomething(fMain); printTime(); printf("Parent:SelfID=%d exit success.\n", getpid()); fflush(fMain); fclose(fMain); return EXIT_SUCCESS; }} |
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信号。
相关文章推荐
- Python入门:类及对象浅析
- new/delete 和malloc/free 的区别一般汇总
- ZOJ 2229 Ride to School
- NodeJS、NPM安装配置步骤(windows版本)
- gluPerspective右手坐标系中透视投影剖析-线性中都要除以-z 和 zn、zf是距离
- AFNetworking3.0
- android 事件派发流程详解
- windows更新npm
- vector利用swap()函数进行内存的释放
- angularjs +boostrap后台模板实现原理
- JAVA反射机制详解
- easyui——一些有用的组件使用小记
- struts+spring+hibernate两张表字段名一样处理方法
- 《构建之法》八、九、十章读后感
- Saltstack常用模块及API
- 用NginX+keepalived实现高可用的负载均衡
- 复用TCP连接提升流媒体服务器之间流量转发效率
- windows更新npm
- java变量及其作用域和调用方式
- Android开发性能优化