《TCP/IP网络编程》第 10 章 多进程服务器端 笔记( 四 )


以下是的使用示例:
#include #include #include int main(int argc, char *argv[]){int status;pid_t pid = fork();if (pid == 0){sleep(15); //用 sleep 推迟子进程的执行return 24;}else{//调用waitpid 传递参数 WNOHANG ,这样之前有没有终止的子进程则返回0while (!waitpid(-1, &status, WNOHANG)){sleep(3);puts("sleep 3 sec.");}if (WIFEXITED(status))printf("Child send %d \n", WEXITSTATUS(status));}return 0;}
编译运行:
gcc waitpid.c -o waitpid./waitpid
结果:

《TCP/IP网络编程》第 10 章 多进程服务器端 笔记

文章插图
可以看出来,在 while 循环中正好执行了 5 次 。这也证明了函数并没有阻塞
10.3 信号处理
我们已经知道了进程的创建及销毁的办法,但是还有一个问题没有解决 。
子进程究竟何时终止?调用函数后要无休止的等待吗?
10.3.1 向操作系统求助
子进程终止的识别主题是操作系统,因此,若操作系统能把如下信息告诉正忙于工作的父进程,将有助于构建更高效的程序
为了实现上述的功能,引入信号处理机制( ) 。此处「信号」是在特定事件发生时由操作系统向进程发送的消息 。另外,为了响应该消息,执行与消息相关的自定义操作的过程被称为「处理」或「信号处理」 。
10.3.2 信号与函数
下面进程和操作系统的对话可以帮助理解信号处理 。
进程:操作系统,如果我之前创建的子进程终止,就帮我调用函数 。
操作系统:好的,如果你的子进程终止,我舅帮你调用函数,你先把要函数要执行的语句写好 。
上述的对话,相当于「注册信号」的过程 。即进程发现自己的子进程结束时,请求操作系统调用的特定函数 。该请求可以通过如下函数调用完成:
#include void (*signal(int signo, void (*func)(int)))(int);/*为了在产生信号时调用,返回之前注册的函数指针函数名: signal参数:int signo,void(*func)(int)返回类型:参数类型为int型,返回 void 型函数指针*/
调用上述函数时,第一个参数为特殊情况信息,第二个参数为特殊情况下将要调用的函数的地址值(指针) 。发生第一个参数代表的情况时,调用第二个参数所指的函数 。下面给出可以在函数中注册的部分特殊情况和对应的函数 。
接下来编写调用函数的语句完成如下请求:
「子进程终止则调用函数」
此时函数的参数应为 int ,返回值类型应为 void。只有这样才能称为函数的第二个参数 。另外,常数定义了子进程终止的情况,应成为函数的第一个参数 。也就是说, 函数调用语句如下:
signal(SIGCHLD , mychild);
接下来编写函数的调用语句,分别完成如下两个请求:
已到通过 alarm 函数注册时间,请调用函数输入 ctrl+c 时调用函数
代表这 2 种情况的常数分别为和,因此按如下方式调用函数 。
signal(SIGALRM , timeout);signal(SIGINT , keycontrol);
以上就是信号注册过程 。注册好信号之后,发生注册信号时(注册的情况发生时),操作系统将调用该信号对应的函数 。先介绍 alarm 函数 。
#include unsigned int alarm(unsigned int seconds);// 返回0或以秒为单位的距 SIGALRM 信号发生所剩时间
如果调用该函数的同时向它传递一个正整型参数,相应时间后(以秒为单位)将产生信号 。若向该函数传递为 0 ,则之前对信号的预约将取消 。如果通过改函数预约信号后未指定该信号对应的处理函数,则(通过调用函数)终止进程,不做任何处理 。