c - 只有当所有 children 都终止时,waitpid() 和 co 是否检测到 child 终止?
c - does waitpid() and co detect child termination only when all children are terminated?
我写这个问题有点困惑,因为我觉得我遗漏了一些要点(毕竟这就是我写它的原因)。
所以我一直在研究多个进程如何访问单个文件。我有一个使 fork
两次的基本代码 - 两次 children fprintf
进入同一个文件,主要区别在于其中一个 sleep
s 并且做更多fprintf
秒。 Child exit
当所有 fprinf
都完成时。同时 parent waitpid
s,每次 child 终止它 fprintf
s 进入同一个文件。
我想看到的是 1) child 进程有更多 fprintf
s 和 sleep
比另一个 child 更晚被终止(我认为差异运行 时间应该提供了一个很好的发生这种情况的可能性)——而且确实发生了; 2) 在文件中间某处查看 parent 进程的第一个 fprintf
作为(我是怎么想的!)第一个 child 应该在 waitpid
之前编辑第二个被终止了——事实并非如此。
每次发生的情况是 fprintf
都被文件末尾的 parent 文件生成到文件中,就像 parent 等待两个children 被终止,然后才 waitpid
ed 他们。
用 wait
交换 waitpid
显然产生相同的结果。
我有几个猜测:
- 第二个 child 终止速度比 parent 有时间
waitpid
第一个fprintf
进入文件。 - OS 没有时间在第二个 child 终止之前将
SIGCHILD
发送到 parent。 - 这就是
waitpid
的工作方式,比如信号是否排队? (但我还没有找到任何此类功能的规范)。
有人能解释一下为什么我没有收到有关第一个 child 在文件中间终止的消息,而是在最后收到它吗?
程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#define N 1000000
#define SLEEP_TIME 20
int main(void)
{
FILE *fd1 = fopen("test.txt", "w");
pid_t pid1, pid2, cpid;
int wstatus;
pid1 = fork();
if(0 == pid1) {
for(int i = 0; i < N; ++i) {
fprintf(fd1, "child1 %d %d\n", getpid(), i);
}
sleep(SLEEP_TIME);
for(int i = 0; i < N; ++i) {
fprintf(fd1, "child1a %d %d\n", getpid(), i);
}
sleep(SLEEP_TIME);
fclose(fd1);
exit(EXIT_SUCCESS);
}
else if(-1 == pid1) {
exit(EXIT_FAILURE);
}
pid2 = fork();
if(0 == pid2) {
for(int i = 0; i < N/2; ++i) {
fprintf(fd1, "child2 %d %d\n", getpid(), i);
}
fclose(fd1);
exit(EXIT_SUCCESS);
}
else if(-1 == pid2) {
exit(EXIT_FAILURE);
}
while(((cpid = wait(&wstatus)) != -1)) {
//while(((cpid = waitpid(-1, &wstatus, WUNTRACED | WCONTINUED)) != -1)) if(WIFEXITED(wstatus))
fprintf(fd1, "child %d exited with status %d\n", cpid, wstatus);
}
if(errno == ECHILD) {
fprintf(fd1, "All children are waited for!\n");
}
else {
perror("waitpid");
exit(EXIT_FAILURE);
}
fclose(fd1);
exit(EXIT_SUCCESS);
}
结果文件的最后几行:
2499998 child1a 7359 999997
2499999 child1a 7359 999998
2500000 child1a 7359 999999
2500001 child 7360 exited with status 0 //I wanted this one to be in the middle of the file!
2500002 child 7359 exited with status 0
2500003 All children are waited for!
不,waitpid 会在每次 child 退出时执行 return。问题是你的测试有缺陷。
在 Unix 上,当您使用 fprintf
等标准输入输出函数访问常规文件时,默认情况下它们是
所以waitpid
实际上是在child2退出后立即returning,那个时候fprintf
正在被调用,但它没有写它立即将消息写入文件;相反,它仍然缓存在 parent 的内存中。它只会在缓冲区填满时被写出(不会发生在 parent,它通常是很多 KB),或者当你调用 fflush
(你没有),或者当文件已关闭(包括进程退出)。因此,当您在 parent 中调用 fclose(fd1)
时,两条消息会一起写出,此时 children 都已退出。
为了更好地说明所发生情况的测试,请在打开文件后立即调用 setvbuf(fd1, NULL, _IONBF, 0)
之类的命令来禁用对此文件的缓冲。然后您应该会在文件中间看到“child2 exited”消息,如您所料。