| 上面的停止程序模拟了一个多进程系统的优雅退出,在这个例子中,这个系统由一个父进程和一个子进程组成。这次模拟的工作流程如下: 
    父进程尝试去 fork一个子进程。假如这个fork操作成功了,每个进程就执行它自己的代码:子进程就执行函数child_code,而父进程就执行函数parent_code。子进程将会进入一个潜在的无限循环,在这个循环中子进程将睡眠一秒,然后打印一个信息,接着再次进入睡眠状态,以此循环往复。来自父进程的一个 SIGTERM信号将引起子进程去执行一个信号处理回调函数graceful。这样这个信号就使得子进程可以跳出循环,然后进行子进程和父进程之间的优雅终止。在终止之前,进程将打印一个信息。在 fork一个子进程后,父进程将睡眠 5 秒,使得子进程可以执行一会儿;当然在这个模拟中,子进程大多数时间都在睡眠。然后父进程调用SIGTERM作为第二个参数的kill函数,等待子进程的终止,然后自己再终止。 下面是一次运行的输出: % ./shutdownParent sleeping for a time...        Child just woke up, but going back to sleep.        Child just woke up, but going back to sleep.        Child just woke up, but going back to sleep.        Child just woke up, but going back to sleep.        Child confirming received signal: 15  ## SIGTERM is 15        Child about to terminate gracefully...        Child terminating now...My child terminated, about to exit myself...
 对于信号的处理,上面的示例使用了 sigaction库函数(POSIX 推荐的用法)而不是传统的signal函数,signal函数有移植性问题。下面是我们主要关心的代码片段: 
    
    假如对 fork的调用成功了,父进程将执行parent_code函数,而子进程将执行child_code函数。在给子进程发送信号之前,父进程将会等待 5 秒: puts("Parent sleeping for a time...");sleep(5);if (-1 == kill(cpid, SIGTERM)) {...sleepkillcpidSIGTERM...
 假如 kill调用成功了,父进程将在子进程终止时做等待,使得子进程不会变成一个僵尸进程。在等待完成后,父进程再退出。
    child_code函数首先调用set_handler然后进入它的可能永久睡眠的循环。下面是我们将要查看的set_handler函数:
 void set_handler() {  struct sigaction current;            /* current setup */  sigemptyset(¤t.sa_mask);       /* clear the signal set */  current.sa_flags = 0;                /* for setting sa_handler, not sa_action */  current.sa_handler = graceful;       /* specify a handler */  sigaction(SIGTERM, ¤t, NULL);  /* register the handler */}
 上面代码的前三行在做相关的准备。第四个语句将为 graceful设定为句柄,它将在调用_exit来停止之前打印一些信息。第 5 行和最后一行的语句将通过调用sigaction来向系统注册上面的句柄。sigaction的第一个参数是SIGTERM,用作终止;第二个参数是当前的sigaction设定,而最后的参数(在这个例子中是NULL)可被用来保存前面的sigaction设定,以备后面的可能使用。 使用信号来作为 IPC 的确是一个很轻量的方法,但确实值得尝试。通过信号来做 IPC 显然可以被归入 IPC 工具箱中。 这个系列的总结在这个系列中,我们通过三篇有关 IPC 的文章,用示例代码介绍了如下机制: 
    共享文件共享内存(通过信号量)管道(命名和无名)消息队列套接字信号 (编辑:源码网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |