【问题标题】:fork() system call in cc中的fork()系统调用
【发布时间】:2012-02-02 08:38:49
【问题描述】:
    #include <stdio.h>
    #include <unistd.h>
    int main()
    {
       fork();
       fork() && fork() || fork();
       fork();

     printf("forked\n");
     return 0;
    }

如何计算程序执行后产生的进程数难以理解? 帮我找出来。

平台--UBUNTU 10.04

【问题讨论】:

  • 为什么不从编译和运行它开始,看看你得到了多少“分叉”消息?
  • 这是作业吗?没有理智的人会写fork() &amp;&amp; fork() || fork()。您可以拆分此行并添加更多调试输出顺便说一句。
  • 我已经编译了很多次,每次得到不同的答案...见这里ideone.com/CXlkR
  • 我猜这是一种“面试问题”。真正的问题是复合线会发生什么。我模糊的回忆是fork() 如果成功则返回TRUE,并且它几乎总是成功,除非你达到了进程的 fork 限制,所以第二行的前两个 fork 调用将执行,但第三行不会执行,因为短-电路。 【啊,我想起来了!! -- fork 对一个进程返回 true 对另一个进程返回 false ,您将在两个进程中继续您离开的地方。结果很可能是一种分形扩展,而且可能是不可预测的。]
  • ideone.com/fDC2a 也见这里

标签: c unix fork


【解决方案1】:

你不应该像这样使用 fork()。绝不。而且,尽管如此,您在现实生活中不需要这样做。 使用方法:

int main() {
    /* code */
    pid_t pid = fork();
    if (pid < 0) {
        /* error, no child process spawned */
    }
    if (pid > 0) {
        /* we are the parent process, pid is the process ID of the ONE child process spawned */
    }
    /* else, we are the child process, running exactly one command later the fork() was called in the parent. */
    /* some more code */
    return 0;
}

【讨论】:

  • 你可能是对的,但这并不能真正回答问题。
  • 嘿,那些反对票的速度变慢了!您显然没有阅读我的答案。在 cmets 中,有已执行或未执行的子进程的数量。我确实想回答他的问题。
【解决方案2】:

保存文件,例如fork-count.c。然后用gcc fork-count.c -o fork-count 编译它。然后你可以运行它并用./fork-count | wc -l 计算输出的行数。

【讨论】:

  • 我编译了很多次,但给出的结果不同。
  • 你的意思是可以连续多次运行程序,得到不同的结果?
  • 如果您使用ideone,您将获得不可靠的结果,因为它无法正确捕获分叉进程的输出。
【解决方案3】:
fork();

fork系统调用返回一个整数:父进程中进程的PID,子进程中为0。如果发生错误,则不创建进程并返回-1。

|| 和 &amp;&amp; 是逻辑运算符。

如果运算符的结果是在对其左操作数求值后知道的,则要求它们短路(即不求右操作数):

  • 对于 || 运算符,如果其左操作数为 != 0,则不计算其右操作数
  • 对于&amp;&amp; 运算符,如果其左操作数为 == 0,则不计算其右操作数

【讨论】:

    【解决方案4】:

    让我们跟随叉树,假设没有一个叉子失败

    fork();

    现在我们有两个进程,到目前为止,谁是孩子谁是父母都没有关系,分别称为 p1 和 p2

    fork()

    这两个进程都产生了另一个子进程,因此我们有 4 个进程,其中两个 (p3, p4) 结果为零,另外两个 (p1 和 p2) 结果为非零

       && fork()
    

    p1 和 p2 再次分叉,得到 p5 和 p6,总共六个进程。在 p1 和 p2 中,&amp;&amp; 的计算结果为 true,因此它们不会在这一行中再次分叉。对于 p3、p4、p5、p6,&amp;&amp; 的计算结果为 false,因此它们分叉

                  || fork();
    

    在这里,产生四个新进程,总共有 6 + 4 = 10。

    fork();

    10 个进程中的每一个再次分叉,得到 20 个。

    【讨论】:

      【解决方案5】:

      我找到了这个问题的正确解释on Geeks for Geeks:

      fork() 系统调用将进程生成为生长二叉树的叶子。如果我们调用 fork() 两次,它将产生 22 = 4 个进程。所有这 4 个过程形成了二叉树的叶子子节点。一般来说,如果我们是第 l 级,并且无条件调用 fork(),我们将在第 (l+1) 级有 2l 个进程。相当于一棵二叉树在第(l+1)层的最大子节点数。

      作为另一个例子,假设我们已经无条件地调用了 3 次 fork() 调用。我们可以使用具有 3 个级别的完整二叉树来表示生成的进程。在第 3 级,我们将有 23 = 8 个子节点,这对应于正在运行的进程数。

      关于 C/C++ 逻辑运算符的说明:

      逻辑运算符 && 比 || 具有更高的优先级,并且具有从左到右的关联性。左操作数执行后,将估计最终结果,右操作数的执行取决于左操作数的结果以及操作类型。

      在 AND (&&) 的情况下,在对左操作数求值后,只有当左操作数的求值结果为非零时,才会对右操作数求值。在 OR (||) 的情况下,在对左操作数求值后,只有当左操作数求值为 0 时才会对右操作数求值。

      fork()的返回值:

      fork() 的手册页引用了以下关于返回值的摘录,

      “成功时,父进程返回子进程的PID,子进程返回0。失败时,在父进程中返回-1,不创建子进程,并适当设置errno。”

      PID 类似于进程的句柄,表示为无符号整数。我们可以得出结论,fork() 将在父项中返回非零值,在子项中返回零值。让我们分析一下程序。为了便于记号,如下所示标记每个 fork(),

           #include <stdio.h>
           int main()
            {
             fork(); /* A */
            (       fork()  /* B */ &&
            fork()  /* C */ ) || /* B and C are grouped according to precedence */
            fork(); /* D */
            fork(); /* E */
      
            printf("forked\n");
            return 0;
           }
      

      前两个 fork() 调用是无条件调用的。

      在级别 0,我们只有主进程。主程序(图中的 m)将创建子 C1,并且两者都将继续执行。孩子们按照他们被创造的升序编号。

      在级别 1,我们有 m 和 C1 正在运行,并准备执行 fork() – B。(注意 B、C 和 D 被命名为 && 和 || 运算符的操作数)。初始表达式 B 将在该级别运行的每个子进程和父进程中执行。

      在级别 2,由于 m 和 C1 执行了 fork() – B,我们将 m 和 C1 作为父级,将 C2 和 C3 作为子级。

      fork() 的返回值 - B 在父节点中非零,在子节点中为零。由于第一个运算符是 &&,由于返回值为零,子 C2 和 C3 将不会执行下一个表达式 (fork()-C)。父进程 m 和 C1 将继续执行 fork() - C。子进程 C2 和 C3 将直接执行 fork() - D,以评估逻辑 OR 操作的值。

      在第 3 级,我们将 m、C1、C2、C3 作为正在运行的进程,将 C4、C5 作为子进程。表达式现在简化为 ((B && C) || D),此时 (B && C) 的值是显而易见的。在父母中它是非零的,而在孩子中它是零。因此,父母知道整体 B && C 的结果 || D,将跳过执行 fork() – D。因为在孩子 (B && C) 评估为零时,他们将执行 fork() – D。我们应该注意,在级别 2 创建的孩子 C2 和 C3,也将运行 fork() - D 如上所述。

      在第 4 级,我们将 m、C1、C2、C3、C4、C5 作为正在运行的进程,将 C6、C7、C8 和 C9 作为子进程。所有这些进程都无条件地执行 fork() - E,并产生一个子进程。

      在第 5 级,我们将运行 20 个进程。该程序(在 Ubuntu Maverick,GCC 4.4.5 上)打印了 20 次“分叉”。一次由根父母(主要)和孩子休息。总共将产生 19 个进程。

      【讨论】:

        猜你喜欢
        • 2017-05-07
        • 1970-01-01
        • 2015-12-22
        • 2011-08-27
        • 1970-01-01
        • 1970-01-01
        • 2011-06-10
        • 1970-01-01
        • 2016-12-14
        相关资源
        最近更新 更多