有人可以解释 fork() 吗?

Can someone explain fork()?

有人可以解释 fork() 并举例说明其用途吗?

我通过一些在线资源了解到的是:

  1. fork() 创建一个与父进程 运行 相同的子进程。
  2. 将创建 2^n 个进程,n = fork() 的数量。
  3. 子进程的 ID 将始终为 0,而父进程的 ID 将是其他值 - 正整数!= 0。

我还有一个问题,首先请看下面我的代码:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main() {
    int id = fork();
    int i = 45;
    fork();
    fork();
    if(id == 0) {
        printf("\nChild Process id = %d",id);
        printf("\ni = %d ; i = %d",i++,i);  
    }
    else {
        printf("\nParent Process id = %d",id);
        printf("\ni = %d ; i = %d",i,i+2);
    }
    return 0;
}   
/*
O/P:
Parent Process id = 9048
i = 45 ; i = 47
Parent Process id = 9048                                                                                                                                                        
i = 45 ; i = 47
Parent Process id = 9048
i = 45 ; i = 47
Parent Process id = 9048
i = 45 ; i = 47
Child Process id = 0
i = 45 ; i = 46
Child Process id = 0
i = 45 ; i = 46
Child Process id = 0
i = 45 ; i = 46
Child Process id = 0
*/

为什么最后一个子进程不显示第二个 printf() 即

printf("\ni = %d ; i = %d",i++,i);

您能解释一下为什么第一个 printf() 第 8 次执行时第二个 printf() 没有执行吗 - 子进程。

fork 的正确用法意味着如果当前进程是 child(fork 返回 0)或 parent(fork 返回 child 的 pid),则立即进行测试,并且按指示行动。在您的情况下, parent 和 child 都启动了一对或虚假进程,这些进程将无法知道它们是什么,因为返回的值被忽略了。其中一些会认为它们是 parent 个进程。

通常情况下,child 进程会执行一个新的可执行文件,如果必须做一些重要的事情的话。

fork() creates child process that would run same as the parent process.

没错。 fork() 创建一个子进程,该子进程是其父进程的副本。


2^n process will be created, n = number of fork().

取决于代码!

int i = 0;
while (i < n)
{
    fork();
    i++;
}

这里,假设没有fork()失败,那么是的,会有2^n个进程。


ID of the Child Process will always be 0, while ID of the Parent Process will be something else - A Positive Integer != 0.

子进程中的fork() return值为0!不是 id,而是 return 值! 父进程中 fork() 的 return 值是子进程的 实际 pid


现在到你的代码

1. int main() {
2.    int id = fork(); 
3.    int i = 45;
4.    fork();
5.    fork();
6.    if(id == 0) {
7.        printf("\nChild Process id = %d",id);
8.        printf("\ni = %d ; i = %d",i++,i);  
9.    }
10.   else {
11.       printf("\nParent Process id = %d",id);
12.       printf("\ni = %d ; i = %d",i,i+2);
13.   }
14.   return 0;
15. } 

第4行,有2个进程

parent         |     child_1
    id > 0     |     id == 0
    i == 45    |     i == 45     

现在调用一个分支

    parent     |     child_1    |    child_OF_1   |   child_2
    id > 0     |     id == 0    |    id == 0      |   id > 0
    i == 45    |     i == 45    |    i == 45      |   i == 45

child_1child_OF_1 将有 id == 0 并输入 if 语句,而 child_2parent 将转到 else 语句并打印 47

我忽略了第 3 个 fork(),到那时你有 8 个进程,其中 4 个将进入 if 语句,而其他将进入 else 块。这就是为什么 你看

i = 45 ; i = 47

4次!

你期望的所有输出都在那里,只是你在后台写出来而没有终止换行符。这意味着某些输出可能会被 shell 提示出现得太早和在程序输出中间遮盖或覆盖。

要确切了解这是如何实现的,请查看程序中某些进程的 strace 输出:

3215092 write(1, "\n", 1)               = 1
3215092 write(1, "Child Process id = 0\n", 21) = 21
3215092 write(1, "i = 45 ; i = 46", 15) = 15
3215094 write(1, "\n", 1)               = 1
3215094 write(1, "Child Process id = 0\n", 21) = 21
3215094 write(1, "i = 45 ; i = 46", 15) = 15

这是 Zsh 绘制提示符的方式。首先是缺少换行指示器,然后是提示本身。特别注意,提示以\r开头,一个回车符return:

3215036 write(10, "[1m[3m%[23m[1m[0m          "..., 306) = 306
3215036 write(10, "\r[0m[23m[24m[Jmyhostname.i"..., 34) = 34

由于您在后台分叉了写入终端的进程,所以您的所有分叉进程都在与 Zsh 竞争以写入它们的输出。

这是一种可能的有效排序,这可能导致:

1. 3215036 write(10, "[1m[3m%[23m[1m[0m          "..., 306) = 306
2. 3215092 write(1, "\n", 1)               = 1
3. 3215092 write(1, "Child Process id = 0\n", 21) = 21
4. 3215092 write(1, "i = 45 ; i = 46", 15) = 15
5. 3215094 write(1, "\n", 1)               = 1
6. 3215094 write(1, "Child Process id = 0\n", 21) = 21
7. 3215094 write(1, "i = 45 ; i = 46", 15) = 15
8. 3215036 write(10, "\r[0m[23m[24m[Jmyhostname.i"..., 34) = 34

步骤 1-7 发生得相当顺利,结果是这样的屏幕(标有 | 的光标):

[...]
i = 45 ; i = 46
Child Process id = 0
i = 45 ; i = 46|

由于没有终止换行符,光标在最后一个字符串之后而不是在最后一个字符串下方结束。

现在,正如您在写入 8 中看到的那样,Zsh 以一个前导回车 return 开始其默认提示符。这意味着将转到行的开头:

[...]
i = 45 ; i = 46
Child Process id = 0
|i = 45 ; i = 46

然后继续覆盖提示行:

[...]
i = 45 ; i = 46
Child Process id = 0
myhostname.internal% 

实际上,即使最后一行输出被写入屏幕,Zsh 提示符通过使用回车 return.

覆盖它来有效地擦除它

您可以使用重定向来输出到文件以更清楚地查看您的程序产生的输出:

./foo > myfile

在编辑器中打开文件显示您的预期结果:

Parent Process id = 3135806
i = 45 ; i = 47
Parent Process id = 3135806
i = 45 ; i = 47
Parent Process id = 3135806
i = 45 ; i = 47
Child Process id = 0
i = 45 ; i = 46
Parent Process id = 3135806
i = 45 ; i = 47
Child Process id = 0
i = 45 ; i = 46
Child Process id = 0
i = 45 ; i = 46
Child Process id = 0
i = 45 ; i = 46

您可以类似地添加自己的终止换行符和延迟以在您的终端中显示输出:

./foo; sleep 5; echo

这给出了与上面相同的结果。

一个正确的程序会输出 after 而不是 before 行,并且每个子进程都会 wait()。 (请注意,如果只修复其中一个,而留下另一个,它将不起作用)