我对以下代码有疑问。这是this page上的示例,而不是我的代码。
父进程分叉2个子进程,每个进程计数到200,然后退出。我不明白的是,为什么孩子们在分叉后立即打印他们的信息并允许他们的父亲进入等待状态? 另外,等待系统调用如何返回首先完成的子进程的pid?
pid =等待(& 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 ***
为什么{1}行显示在开头而不是儿童开始打印后的某个地方?
答案 0 :(得分:3)
Fork创建一个新进程,可以与父进程同时(或交错)执行。它不会使父进程停止。在fork
调用之后,两个进程都是&#34; runnable&#34;,它们很可能都在运行。如果父进程是幸运的,那么它将是第一个能够输出的进程。一般来说,这是不可预测的。
该代码中存在许多错误,因此我不会将其视为学习如何使用fork()
的良好来源。
例如,父进程使用printf
来编写消息:
*** Parent is about to fork process 1 ***
*** Parent is about to fork process 2 ***
但是,stdout
很可能不行缓冲(例如,它可能被重定向。)在这种情况下,输出只会被复制到-memory输出缓冲区,当缓冲区填满或stdout
文件描述符关闭时,将打印到stdout
。但是,子节点将使用相同的内存输出缓冲区进行分叉,因此父节点和子节点将输出*** Parent is about to fork process 1 ***
消息,并且所有三个进程都将输出*** Parent is about to fork process 2 ***
消息。
此外,如果stdout
被重定向到未在追加模式下打开的可搜索流,则每个进程的文件描述符1将具有相关联的文件位置,每个位置将独立地操作。这可能导致两个子进程覆盖彼此的输出。 (在我的系统上,覆盖发生的时间大约是80%。)
答案 1 :(得分:0)
在非繁忙的多核或多处理器系统上,孩子很可能几乎立即开始执行。在Unix和Linux上,分叉是一种快速简便的操作:复制一些内存描述符,复制一些资源,修复一些信号逻辑并使两个进程都可以运行。调度程序是不可预测的(在此级别的观察中):它旨在公平公正。
此外,孩子们做的更多是计算量大的格式化文本,在CRTL中缓冲它,可能将其转移到i / o系统 - 比父母做的更多:fork(),比较数字,wait()
因此,父母很快就会进入wait()就不足为奇了,因为它不再竞争CPU时间。
每个带有printf()
的{{1}}可能(或可能不会)通过从混合中删除两个级别的i / o缓冲来提高一致性。