【C/C++】多进程:信号量的监听与处理函数
来源:互联网 发布:软件架构设计图 编辑:程序博客网 时间:2024/06/18 08:25
文章结构:
- 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
信号。
- 【C/C++】多进程:信号量的监听与处理函数
- 多进程:信号量的监听与处理函数
- linux进程间的通信(C): 信号量
- 【C/C++】多进程:父进程监听子进程状态 wait()的使用
- C++windows内核编程笔记day13 进程、线程与信号量
- C 信号量与互斥锁的区别
- linux的进程通信:信号量实例(C语言)
- Linux C——信号量进程通信
- 【C#-Socket监听消息处理】
- C#-Socket监听消息处理
- C#-Socket监听消息处理
- Perl多进程与信号量
- 【语言-C#】进程处理
- C的字符处理函数
- C++与C中的函数互相调用的处理
- C++与C中的函数互相调用的处理
- C语言的字符与字符串处理函数
- linux C进程 进程等待wait与waitpid函数
- 黑马程序员--继承,覆盖,抽象类,接口,多态。
- javaweb项目从svn检出变成java项目
- 【LeetCode刷题记录】Number of 1 Bits
- 【C/C++】多进程:僵尸进程
- 数位DP
- 【C/C++】多进程:信号量的监听与处理函数
- android锁屏
- 反转链表(reverse a lnked list)
- C# Windows API应用之FlashWindowEx ——实现窗口闪烁的方法
- 在Android虚拟机中推送文件显示readonly解决方法
- block 定义及简单用法
- iOS CoreText类库的详细介绍
- 通达OA-今日学习 精灵报错提示初始化失败
- linux一些常用iptables防火墙规则整理收集