有时需要等待……

newshound2程序启用独立的进程运行rssgossip.py脚本,而子进程一创建就和父进程没关系了。rssgossip.py还没有完成任务,newshound2程序就结束了,所以stories.txt还是空的。也就是说,操作系统必须提供一种方式,让你等待子进程完成任务。

有时需要等待…… - 图1

waitpid()函数

waitpid()函数会等子进程结束以后才返回,也就是说可以在程序中加几行代码,让它等到rssgossip.py脚本运行结束以后才退出。

有时需要等待…… - 图2

有时需要等待…… - 图3waitpid()聚焦

waitpid()接收三个参数:

有时需要等待…… - 图4

  • pid

    父进程在克隆子进程时会得到子进程的ID。

  • pid_status

    pid_status用来保存进程的退出信息。因为waitpid()需要修改pid_status,因此它必须是个指针。

  • 选项

    waitpid()有一些选项,详情可以输入man waitpid查看。如果把选项设为0,函数将等待进程结束。

什么是pid_status?

waitpid()函数结束等待时会在pid_status中保存一个值,它告诉你进程的完成情况。为了得到子进程的退出状态,可以把pid_status的值传给WEXITSTATUS()宏:

有时需要等待…… - 图5

为什么要用宏来查看?因为pid_status中保存了好几条信息,只有前8位表示进程的退出状态,可以用宏来查看这8位的值。

有时需要等待…… - 图6试驾

运行newshound2程序,它会在退出前检查rssgossip.py脚本是否完成:

有时需要等待…… - 图7

在程序中加入waitpid()很容易办到,它可以让代码更加可靠。而在此之前无法确定子进程是否已经写完了文件,也就是说newshound2并不是一个合格的工具,你无法在脚本中使用它,也无法为它创建界面。

重定向输入、输出,然后让进程相互等待,进程间通信就这么简单。一旦进程可以合作——通过共享数据和互相等待——它们将所向披靡。

有时需要等待…… - 图8

有时需要等待…… - 图9要点

  • exit()可以快速结束程序。

  • 所有打开的文件都记录在描述符表中。

  • 通过修改描述符表就可以重定向输入和输出。

  • fileno()能在表中查找描述符。

  • dup2()可以用来修改描述符表。

  • waitpid()等待进程结束。

 

这里没有蠢问题

问:用exit()来结束程序比从main()返回更快吗?

:不会。但如果你已经调用了exit(),就不需要想办法让代码再回到main()函数。在你调用exit()的一瞬间,程序就升天了。

问:为了防止调用失败,调用exit()时需要检查它的返回值是否为-1吗?

:不需要,exit()不会失败,因此也就没有返回值。exit()是唯一没有返回值而且不会失败的函数。

问:传给exit()的那个数字是退出状态吗?

:是的。

问:标准输入、标准输出和标准错误一定是描述符表的0、1、2号吗?

:一定。

问:每当我打开一个新文件,它都会自动添加到描述符表中吗?

:没错。

问:通常是几号描述符?

:新文件总是按序加入描述符表,如果第一个空的描述符是4号,你的文件就会用它。

问:描述符表有多大?

:从0号到255号。

问:描述符表那么麻烦,有必要用吗?

:当然有,不使用描述符表,就不能改变程序的工作方式,也就不能重定向。

问:除了用标准输出,还有没有其他方法把数据发送到屏幕?

:在一些系统上,比如Unix,如果打开/dev/tty文件,就可以把数据直接发送到终端。

问:我能用waitpid()等待其他进程吗?还是只有我启动的那些?

:你可以用waitpid()等待任何进程。

问:为什么不能根据wait_pid(…, &pid_status, …)中的pid_status直接判断退出状态?

:因为pid_status中还包含了其他信息。

问:哪些信息?

:如果一个进程自然死亡,WIFSIGNALED(pid_status)就为假,如果是他杀,WIFSIGNALED(pid_status)就为真。

问:pid_status明明是整型变量,怎么可以包含多条信息?

:用不同的位来保存不同的信息。pid_status的前8位保存了退出状态,而其他信息保存在了剩余那些位中。

问:是不是只要自行提取出pid_status的前8位,就可以不用WEXITSTATUS()

:最好还是用WEXITSTATUS(),它不但可以提高代码的可读性,而且无论int在你的平台上有多大,程序都能正确工作。

问:为什么WEXITSTATUS()要大写?

:因为WEXITSTATUS()是宏,不是函数。编译器运行时会把宏替换为一小段代码。