当下面的代码运行时,我理解父和子都将在调用fork()后立即运行并行。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
int pfds[2];
char buf[30];
pipe(pfds);
if (!fork()) {
printf(" CHILD: writing to the pipe\n");
write(pfds[1], "test", 5);
printf(" CHILD: exiting\n");
exit(0);
} else {
printf("PARENT: reading from pipe\n");
read(pfds[0], buf, 5);
printf("PARENT: read \"%s\"\n", buf);
wait(NULL);
}
return 0;
}
这意味着孩子将执行:
printf(" CHILD: writing to the pipe\n");
父母将执行
printf("PARENT: reading from pipe\n");
并行。
对于那些不熟悉C
的人,请sh
:
$ sh -c 'echo CHILD & echo PARENT; wait'
PARENT
CHILD
所以我在单核心cpu和双核上运行此代码,但我注意到父母总是首先打印。由于两者并行,因此期望随机顺序是合理的。但事情并非如此。为什么会那样?
答案 0 :(得分:3)
显然,您正在运行的任何调度程序都会决定,并且它可能会有所不同。
我可以从经验中说,如果你假设两个进程中的一个总是先运行,你会引入一些非常微妙的竞争条件。要么对某些东西进行同步,比如管道上的特殊消息,要么不要假设任何一个先运行。
答案 1 :(得分:2)
我认识到这可能不能完全回答这个问题,但它可能有助于揭示正在发生的事情:
if (!fork()) {
printf(" CHILD: writing to the pipe\n");
write(pfds[1], "test", 5);
printf(" CHILD: exiting\n");
exit(0);
} else {
printf("PARENT: reading from pipe\n");
read(pfds[0], buf, 5);
printf("PARENT: read \"%s\"\n", buf);
wait(NULL);
}
执行fork之后,父进程继续运行,然后进入else语句,并执行printf函数。之后它会尝试从管道读取,但由于管道中没有数据,它会阻塞。没错,read()
在尝试从没有数据的管道中读取时会阻塞。
用于证明此功能的文档可用。来自xv6文档:
如果没有可用数据,则管道上的读取将等待数据 写入或所有文件描述符引用写入结束 关闭;在后一种情况下,读取将返回0,就像结束一样 已达到数据文件。
虽然xv6可能不是Linux,但它可以作为UNIX的设计指南。如果您认为这不够有效,那么the linux Man pages on pipes can shed some light:
如果进程尝试从空管读取,则读(2)将 阻止,直到数据可用。如果进程尝试写入 完整管道(见下文),然后写入(2)块,直到有足够的数据 已从管道中读取以允许写入完成。非阻塞 通过使用fcntl(2)F_SETFL操作来启用I / O. O_NONBLOCK打开文件状态标志。
之后,控制权传递给孩子,孩子继续执行printf()
函数的版本, write()
到管道,然后打印退出消息,最后退出。
当孩子退出时,控制权再次传递给父流程,在管道上找到read()
以便能够读取数据,这样就可以完成其工作。
至于
并行
部分而言,它似乎并不完全平行。这是串行的,我们很快就会注意到它是按顺序执行的。