【问题标题】:fork() and wait() callsfork() 和 wait() 调用
【发布时间】:2015-03-30 07:58:10
【问题描述】:

我对以下代码有疑问。这是在this page 上找到的示例,不是我的代码。

父进程派生出 2 个子进程,每个子进程计数到 200 然后退出。我不明白的是,为什么孩子们在分叉后不立即打印他们的消息并让他们的父亲进入等待状态? 另外,等待系统调用如何返回首先完成的孩子的 pid?

pid = wait(&status);

#include  <stdio.h>
#include  <string.h>
#include  <sys/types.h>

#define   MAX_COUNT  200
#define   BUF_SIZE   100

void  ChildProcess(char [], char []);    /* child process prototype  */

void  main(void)
{
     pid_t   pid1, pid2, pid;
     int     status;
     int     i;
     char    buf[BUF_SIZE];

     printf("*** Parent is about to fork process 1 ***\n");
     if ((pid1 = fork()) < 0) {
          printf("Failed to fork process 1\n");
          exit(1);
     }
     else if (pid1 == 0) 
          ChildProcess("First", "   ");

     printf("*** Parent is about to fork process 2 ***\n");
     if ((pid2 = fork()) < 0) {
          printf("Failed to fork process 2\n");
          exit(1);
     }
     else if (pid2 == 0) 
          ChildProcess("Second", "      ");

     sprintf(buf, "*** Parent enters waiting status .....\n");
     write(1, buf, strlen(buf));
     pid = wait(&status);
     sprintf(buf, "*** Parent detects process %d was done ***\n", pid);
     write(1, buf, strlen(buf));
     pid = wait(&status);
     printf("*** Parent detects process %d is done ***\n", pid);
     printf("*** Parent exits ***\n");
     exit(0);
}

void  ChildProcess(char *number, char *space)
{
     pid_t  pid;
     int    i;
     char   buf[BUF_SIZE];

     pid = getpid();
     sprintf(buf, "%s%s child process starts (pid = %d)\n", 
             space, number, pid);
     write(1, buf, strlen(buf));
     for (i = 1; i <= MAX_COUNT; i++) {
          sprintf(buf, "%s%s child's output, value = %d\n", space, number, i); 
          write(1, buf, strlen(buf));
     }
     sprintf(buf, "%s%s child (pid = %d) is about to exit\n", 
             space, number, pid);
     write(1, buf, strlen(buf));     
     exit(0);
}

MAX_COUNT 40 的输出。

*** Parent is about to fork process 1 ***
*** Parent is about to fork process 2 ***
*** Parent enters waiting status .....
   First child process starts (pid = 3300)
   First child's output, value = 1
      Second child process starts (pid = 3301)
      Second child's output, value = 1
   First child's output, value = 2
      Second child's output, value = 2
      Second child's output, value = 3
   First child's output, value = 3
      Second child's output, value = 4
   First child's output, value = 4
      Second child's output, value = 5
   First child's output, value = 5
      Second child's output, value = 6
   First child's output, value = 6
      Second child's output, value = 7
   First child's output, value = 7
      Second child's output, value = 8
   First child's output, value = 8
      Second child's output, value = 9
   First child's output, value = 9
      Second child's output, value = 10
   First child's output, value = 10
      Second child's output, value = 11
   First child's output, value = 11
      Second child's output, value = 12
   First child's output, value = 12
      Second child's output, value = 13
   First child's output, value = 13
      Second child's output, value = 14
   First child's output, value = 14
      Second child's output, value = 15
   First child's output, value = 15
      Second child's output, value = 16
   First child's output, value = 16
      Second child's output, value = 17
   First child's output, value = 17
      Second child's output, value = 18
   First child's output, value = 18
      Second child's output, value = 19
   First child's output, value = 19
      Second child's output, value = 20
   First child's output, value = 20
      Second child's output, value = 21
   First child's output, value = 21
      Second child's output, value = 22
   First child's output, value = 22
      Second child's output, value = 23
   First child's output, value = 23
      Second child's output, value = 24
   First child's output, value = 24
      Second child's output, value = 25
   First child's output, value = 25
      Second child's output, value = 26
   First child's output, value = 26
      Second child's output, value = 27
   First child's output, value = 27
      Second child's output, value = 28
   First child's output, value = 28
      Second child's output, value = 29
   First child's output, value = 29
      Second child's output, value = 30
   First child's output, value = 30
      Second child's output, value = 31
   First child's output, value = 31
      Second child's output, value = 32
   First child's output, value = 32
      Second child's output, value = 33
   First child's output, value = 33
      Second child's output, value = 34
   First child's output, value = 34
      Second child's output, value = 35
   First child's output, value = 35
      Second child's output, value = 36
   First child's output, value = 36
      Second child's output, value = 37
   First child's output, value = 37
      Second child's output, value = 38
   First child's output, value = 38
      Second child's output, value = 39
   First child's output, value = 39
      Second child's output, value = 40
   First child's output, value = 40
      Second child (pid = 3301) is about to exit
   First child (pid = 3300) is about to exit
*** Parent detects process 3300 was done ***
*** Parent detects process 3301 is done ***
*** Parent exits ***

为什么*** Parent enters waiting status ..... 行显示在开头而不是在孩子开始打印后的某个位置?

【问题讨论】:

  • “为什么孩子们在分叉后不立即打印他们的消息并让他们的父亲进入等待状态”是什么意思?确切地。目前还不完全清楚您的问题是什么。
  • 您是否知道fork() 正在创建新进程(不破坏第一个进程),并且多个进程可以同时运行?如果是这样,我不确定你在问什么。
  • @ian-sellar @dmitri fork() 创建另一个进程,在我们的例子中,因为它是一个子进程,所以进入 ChildProcess("First", " ");。现在,为什么不从 1 打印到 200?为什么消息 *** Parent enters waiting status ..... 没有显示在 1-200 之间或末尾?
  • 在包含 3 个缺失的标头(sys/wait.hstdlib.hunistd.h)并修复 main 的返回类型(应该是 int)后,它按我的预期工作。 ..所有三个进程都运行,它们的输出是交错的,等等。新进程需要一点时间才能开始(例如,第一个孩子在第二个开始计数之前计数到 147)......但这是预期的,因为启动新进程所涉及的开销。

标签: c unix fork wait


【解决方案1】:

Fork 创建一个新进程,该进程可以与父进程同时(或交错)执行。它不会使父进程停止。在fork 调用之后,两个进程都是“可运行的”,而且很可能它们都在运行。如果父进程幸运的话,它将是第一个能够输出的。一般来说,它是不可预测的。

该代码中存在许多错误,因此我不会将其视为学习如何使用 fork() 的好来源。

例如父进程使用printf写消息:

*** Parent is about to fork process 1 ***
*** Parent is about to fork process 2 ***

但是,stdout 很有可能是不是行缓冲的(例如,它可能会被重定向。)在这种情况下,输出只会被复制到内存中输出缓冲区,并在缓冲区填满或stdout 文件描述符关闭时打印到stdout。但是,子进程将使用相同的内存输出缓冲区进行分叉,因此父进程和子进程都将输出*** Parent is about to fork process 1 *** 消息,并且所有三个进程都将输出*** Parent is about to fork process 2 *** 消息。

此外,如果stdout 被重定向到未以附加模式打开的可搜索流,则每个进程的文件描述符 1 将具有关联的文件位置,每个进程都将独立操作该位置。这可能会导致两个子进程覆盖彼此的输出。 (在我的系统上,大约 80% 的时间发生了覆盖。)

【讨论】:

  • 仅作为参考。在带有 openSuSE 的较旧双核 x86_64 上,第一个子进程 (fcp) 在父进程进入等待之前启动并输出 1-9。 fcp 继续按顺序输出 10-200。 fcp 即将退出,第二个子进程 (scp) 从顺序输出 1-174 开始,其中父进程检测到 fcp 已完成。 scp 按顺序继续 175-200。我怀疑这完全取决于每台机器的处理器/调度程序/负载。
  • @DavidC.Rankin:完全。甚至没有提到月相。 “一般来说,它是不可预测的。”
【解决方案2】:

在非繁忙的多核或多处理器系统上,子级很可能几乎立即开始执行。在 Unix 和 Linux 上,fork 是一种快速而简单的操作:复制一些内存描述符,复制一些资源,修复一些信号逻辑并让两个进程都可以运行。调度程序是不可预测的(在这个观察级别上):它被设计成公平公正的。

此外,孩子们所做的计算量更大——格式化文本、在 CRTL 中缓冲它、可能将其传输到 i/o 系统——而不是父级所做的:fork()、比较数字、wait() .

因此,父母很快到达它的 wait() 并不再竞争 CPU 时间也就不足为奇了。

在每个printf() 后面加上fflush(stdout) 可能会(也可能不会)通过从混合中移除两级I/O 缓冲来提高一致性。

【讨论】:

  • children中的代码没有printf;它 sprintf()s 然后 write()s。目的是避免混合输出和缓冲问题,但正如我在回答中解释的那样,它实际上并没有解决问题。无论如何,刷新或行缓冲将是一个好主意,但它只适用于父打印的几行。父母和孩子做的第一件事大致相同:将文本格式化到缓冲区中,然后在该缓冲区上调用 write(它不会在用户空间中进行额外的缓冲),因此第一条输出行的生成应该是有竞争力的。
猜你喜欢
  • 2014-04-21
  • 2012-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-12
  • 2015-11-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多