叉子的流程,我有多少个叉子?

Posted

技术标签:

【中文标题】叉子的流程,我有多少个叉子?【英文标题】:Flow of the fork, how many forks do I have? 【发布时间】:2016-10-23 21:11:05 【问题描述】:

我已经执行了这段代码。 我知道消息的顺序是任意的(因为我明确没有使用信号量) 我的程序流程看起来如何,为什么?

父级被执行,所以“baz”被打印一次。有人可以解释为什么不打印“bar”吗?为什么我得到“foo”(if 语句为真)两次而不是一到三次(不是我想要这个但我想了解逻辑)(因为一位同事说我应该得到三倍 foo它)?

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() 
  int p;
  p = fork();
  if (fork()==0) 
    if (execl("/bin/echo", "/bin/echo", "foo", 0) == -1) 
      fork();
    
  printf("bar\n");
  
  else 
    if (p!=0) execl("/bin/echo", "/bin/echo", "baz", 0);
  

【问题讨论】:

只是对未来问题的提示。为了避免投票(我不是其中之一),如果问题是“如果我只是阅读man page,我可能会弄清楚......”,你可能应该先尝试一下。这里的人很乐意帮助您解决编程问题,但不是来解释如何阅读基本文档。虽然这个问题确实显示了努力,但它可能属于基本信息方面。 你可以从字面上观察这个程序做了什么,要么使用strace -f,要么插入调试打印(包括getpid()的值,这样你就知道它是哪个进程)。 【参考方案1】:

execl 不返回,它将整个过程映像替换为 /bin/echo。 因此有零个“条”。

    if (execl("/bin/echo", "/bin/echo", "foo", 0) == -1) 
      fork();
    
    /* Not reached if execl succeeded.
    Because the exec family of functions replace the process image with
    another executable.  Flow will never return, unless there is an
    error. */
    printf("bar\n");

有两个“foo”。

  int p;
  p = fork();
  /* Two processes now */
  if (fork()==0) 
      /* Two child processes here. */
      execl("/bin/echo", "/bin/echo", "foo", 0); /* (Simplification) */
      /* Two (/bin/echo foo) here, flow will never return back */
  

只有一个“baz”。

int p;
p = fork();
/* if block removed for simplicity */
if (p != 0)

    /* Only the initial parent process. */
    execl("/bin/echo", "/bin/echo", "baz", 0);

【讨论】:

foo 的数量呢? 我应该在程序中得到两到三倍的 foo 吗? p = fork(); 产生一个新进程(总共 2 个)。其中每一个都运行if (fork() == 0),它会产生另外两个进程。原来的 2 没有进入 if 语句,因为 fork 在其中返回了一个非零值。因此只有 2 个最新进程运行 execl 所以你的意思是我的同事错了?而且我应该有两次 foo 和一次 baz。 @TanjaMeeren “应该”是什么意思?您已经在原始问题中向我们提供了事实:baz 打印一次,bar 根本不打印,foo 打印两次。你已经知道你的同事错了。【参考方案2】:

首先您需要了解exec 系列系统调用将整个程序替换为其他程序。在您的情况下,“回声”程序。除了execl 调用之外的任何内容都不会执行。

这是正在发生的事情:

    您的父进程(称为 p0)执行 p=fork(),它派生出一个克隆进程 p1。 p0 再次执行 if (fork()==0) 派生另一个克隆进程 p2

    p0 然后执行打印“baz”的 else 语句的主体。

    在被分叉后,p1 执行if (fork()==0) 语句,该语句分叉另一个进程 p3。与 p0 一样,p1 将进入 else 语句但不会打印“baz”,因为在 p0 分叉时设置了 p p1 实际上等于 0(因为 p1 是 p0 的孩子)。

    p2 进入 if 语句体并执行 execl 函数,将当前程序替换为打印“foo”的 echo 程序。

    与 p2 一样,p3 进入 if 语句体并执行 execl 函数,该函数将当前程序替换为打印“foo”的 echo 程序"。

【讨论】:

【参考方案3】:

第一个fork 创建两个进程, 然后两者都进行第二次分叉。

p0    (p=$pid)              //first fork
                p1  (p==0)  

   p01                p11      //second fork
      exec              exec

在叶子子节点(p01p11)中,第二个 fork 后面跟着一个 exec,如果成功,则结束旧的进程映像。这应该在stdout 上给你两个foos。

父母(p0p1)然后执行以下操作:

if (p!=0) execl("/bin/echo", "/bin/echo", "baz", 0);

p!=0 测试可能只在p0(原始过程)中成功。 这应该会在stdout 上为您提供一个baz


(在实际代码中,您还应该检查fork 错误)。

【讨论】:

以上是关于叉子的流程,我有多少个叉子?的主要内容,如果未能解决你的问题,请参考以下文章

Git Bash 错误:无法分叉子进程:权限被拒绝(-1)

LeetCode——哲学家进餐问题

叉子过程不从管道读取

二叉树——套路化解题--1.最大搜索二叉子树

IPC之哲学家进餐问题

如何要求作曲家的叉子?