为什么这个 Forking 的例子不需要 mutex?

Why does this example of Forking not need mutex?

我的教授有一个函数的示例代码,它分叉以充当管道。但是他如何确保父进程在子进程之前执行而不必使用互斥锁呢?

void
runpipe(int pfd[])
 {
int pid;

switch (pid = fork()) {

case 0: /* child */
    dup2(pfd[0], 0);
    close(pfd[1]);  /* the child does not need this end of the pipe */
    execvp(cmd2[0], cmd2);
    perror(cmd2[0]);

default: /* parent */
    dup2(pfd[1], 1);
    close(pfd[0]);  /* the parent does not need this end of the pipe */
    execvp(cmd1[0], cmd1);
    perror(cmd1[0]);

case -1:
    perror("fork");
    exit(1);
}
}

通常先 child 还是 parent 运行 是无关紧要的。

如果 parent execvp() 首先开始写入管道,一旦管道变满,写入将阻塞,直到 child 读取一些数据。

如果 child execvp() 首先开始从管道读取,读取同样会阻塞,直到 parent 写入一些数据。

作为旁注,parent 将首先在现代 Linux 内核(例如 3.16.0)上 fork() 之后 运行,尽管这不应该被依赖。 (此外,parent 的时间片可能 运行 紧跟在 fork() 之后,使其看起来像 child 运行 在前。)一些 2.4 内核运行先child代替。

child 关闭管道的写入端和 parent 管道的读取端的原因是为了确保管道的每一端只有一个文件描述符保持打开状态。 (请记住,fork(2) 复制文件描述符。)这意味着一旦 parent 关闭写入端,child 将看到 end-of-file。如果 child 关闭了读取端,而 parent 之后尝试写入管道,则 parent 将收到 SIGPIPE.

代码不确保 parent 在 child 之前执行(从分叉点开始)。一般来说,没有必要在这个级别强制执行这样的要求。

如果 child 进程要使用写入管道的数据,那么它可能会阻止尝试从管道读取数据,直到 parent 写入数据。我说 "probably" 是因为 child 可能有意表现不同。然而,两个进程可以同时 运行 直到那个点,或者 child 可以在 parent 之前 运行 直到它阻塞。如果这是一个问题,exec() 由 parent 和 child 编辑的程序必须自行解决。