【问题标题】:In fork() which will run first, parent or child?在 fork() 中哪个会先运行,父母还是孩子?
【发布时间】:2014-03-02 10:01:37
【问题描述】:

当下面的代码运行时,我知道父子都将在调用 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 和双核 cpu 上运行此代码,但我注意到 parent 总是首先打印。由于两者是并行的,因此期望随机顺序是很合理的。但事实并非如此。为什么会这样?

【问题讨论】:

  • 这个问题在这里合适还是应该移到stackoverflow?
  • 这里很合适,这不是编程问题,而是关于给定 Unix/Linux 调度程序行为的问题。
  • 在 Linux 3.12 上,如果我有 sudo sysctl -w kernel.sched_child_runs_first=1 和其他一些,我有时可以使用 sh -c 'sleep 1; echo CHILD &amp; echo PARENT; wait' 让孩子先运行(也就是说,在分叉之前让 sh 进程空闲一段时间) CPU 密集型进程同时运行。考虑到许多参数,调度算法是相当复杂的。我想说在这里抱任何期望是毫无意义的。
  • 您应该重写您的问题并删除 pipe 方面,因为它与此处无关以避免混淆。
  • @user25108 考虑到任何 shell 在 fork 操作之后可能是 yielding parent、child、both 或 none。

标签: fork


【解决方案1】:

显然,您正在运行的调度程序决定了,并且可能会有所不同。

我可以根据经验说,如果您假设两个进程中的一个总是首先运行,您将引入一些非常微妙的竞争条件。要么同步某些东西,比如管道上的特殊消息,要么不要假设其中任何一个先运行。

【讨论】:

  • +1 它取决于调度程序它想要执行父进程和子进程的顺序。
  • 为什么调度程序不能并行运行进程,特别是因为我们现在有多核?
  • @user2799508 - 多核机器上的调度程序当然可以做到这一点。我的观点是,你就是说不出来。不要假设。要么同步某些东西,比如创建目录,要么就可能影响其他进程的操作编写“线程安全”的代码。
【解决方案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 文档:

如果没有可用数据,则管道上的读取将等待任一数据 写入或所有引用写入端的文件描述符是 关闭;在后一种情况下,read 将返回 0,就像 end 已达到数据文件的大小。

虽然 xv6 可能不是 Linux,但它可以作为 UNIX 的设计指南。如果您认为这不够有效,那么the linux Man pages on pipes can shed some light

如果一个进程试图从一个空管道中读取,那么 read(2) 将 阻塞直到数据可用。如果一个进程试图写入一个 完整的管道(见下文),然后 write(2) 阻塞,直到有足够的数据 从管道中读取以允许写入完成。非阻塞 I/O 可以通过使用 fcntl(2) F_SETFL 操作来启用 O_NONBLOCK 打开文件状态标志。

之后,控制权传递给子进程,子进程继续执行其版本的printf() 函数,write()s 到管道,然后打印退出消息,最后退出之后。

当子进程退出时,控制权再次传递给父进程,在管道上找到read() 以便能够读取数据,这允许它完成它的工作。

至于

并行

就部分而言,它似乎并不完全平行。它是串行的,我们很快就会注意到它是以串行执行顺序完成的。

【讨论】:

  • 它不平行你是对的@NlightNFotis。但是执行(子将首先执行或父执行)取决于调度程序。
  • @vishram0709 是的,我知道这一点。我以为我已经提到了它,但回想起来,我没有看到任何地方提到它。我不知何故错过了提及它。
  • fork(2) 具有您应该处理的三种类型的返回值 0
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-26
  • 1970-01-01
  • 2019-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-22
相关资源
最近更新 更多