c语言 - 这个程序中涉及fork()系统调用的控制流是如何的?



从我读到的关于系统调用的信息fork()

fork 系统调用用于创建一个新进程,称为子进程,它与父进程同时运行

创建新的子进程后,两个进程都将在 fork() 系统调用之后执行下一条指令

fork()向子进程返回 0

fork()将新创建的子进程的进程 ID 返回到父进程(正值)

如果子进程创建失败,fork()返回负值

在这一段代码中

void foo() { 
if (fork() == 0) 
printf("Hello from Child!n"); 
else 
printf("Hello from Parent!n"); 
} 
int main() { 
foo(); 
return 0; 
} 

输出为

Hello from Parent!
Hello from Child!

当控件处于主进程中函数 foo 的if-else条件内时,将创建子进程。

那么子进程是从哪里(哪条指令)开始执行的呢?

从输出中可以看出,当fork()返回0时,将打印Hello from Parent。所以据我了解Hello from Parent实际上是由儿童进程打印的

fork()向父进程返回一个正值,父进程打印Hello from Child。我对此的理解正确吗?

子进程究竟是从哪条指令开始执行的?对fork()的函数调用是在if-else的条件部分中给出的。所以孩子应该在那if-else之后开始执行,但这不是正在发生的事情吗?

让我们从这里确定一个主要的误解开始:

从输出中可以看出,当 fork() 返回 0 时,将打印来自父级的 Hello 。所以据我了解,来自父母的你好实际上是由子进程打印的

子进程和父进程是同时运行的两个独立进程。这两个输出的顺序没有明确定义,会根据您的内核和其他计时注意事项而有所不同,并且与您的代码包含按原样编写的 if/else 块这一事实无关。1

让我们将您的代码重写为抽象意义上的线性"指令"流:

0: Function foo():
1:  Invoke system call fork(), no arguments, store result to $1
2:  If $1 is non-zero, jump to label #1.
3:  Invoke C function printf(), argument "Hello from Child!"
4:  Jump to label #2.
5: Label #1:
6:  Invoke C function printf(), argument "Hello from Parent!"
7: Label #2:
8: return control to calling function.

一旦程序达到1:,就会调用系统调用,将控制权转移到内核。内核复制进程,将子进程的PID放入父进程中fork的返回值中,0放入子进程的返回值中fork。在 x86 上,返回值作为 syscall 调用约定的一部分存储在寄存器eax(x64 为rax)中。

这两个进程之一最终将被安排为由内核运行。在您的情况下,子进程恰好是第一个被安排的进程。您的用户模式代码从内核模式收回控制权,读取返回值(如果在 x86 上,则为 eax/rax 之外),该值为零,并且没有跳转到标签 #1。它打印Hello from Child!,然后从函数返回(返回给foo的调用者,因为子级得到了父堆栈的副本)。

父级也发生了同样的情况,只是父级从系统调用中返回了一个非零值,并打印了Hello from Parent!。它被安排运行,并且您的用户模式代码在同一点从内核获得控制权,只是系统调用返回的值不同。

1这两个输出也可能以某种方式交错,但这与本次讨论无关,需要了解 Linux 进程如何执行 I/O。

子进程是并行执行的第二个进程。你可能很容易得到

Hello from Child!
Hello from Parent!

例如,如果您打开了一个终端窗口,并且启动firefox &,哪个运行"首先",终端窗口还是浏览器窗口? 两者同时运行。

事实上,Linux 在重新处理父进程之前会稍微启动子进程。 这是因为大量调用fork()的程序会立即让子程序exec()程序,从而使父程序无需与子程序共享其所有内存。这更有效,因为共享内存是写入时复制的。

最新更新