根据我对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
条件内时,将创建子进程。
那么子进程从哪里(哪个指令)开始执行?
从输出中可以看到, Hello from Parent
返回fork()
时将打印0
。因此,据我了解,Hello from Parent
实际上是由子进程打印的
fork()
向父进程返回一个正值,并且父进程打印Hello from Child
。我对此的理解正确吗?
子进程究竟从哪条指令开始执行的?在fork()
的条件部分中给出了对if-else
的函数调用。因此孩子应该在if-else
之后开始执行,但这不是正在发生的事情吗?
答案 0 :(得分:1)
子进程是第二个并行执行的进程。您可能同样容易得到
Hello from Child!
Hello from Parent!
例如,如果您打开了终端窗口,并且启动了firefox &
(首先运行),那么终端窗口还是浏览器窗口呢?两者同时运行。
实际上,Linux在重新启动父进程之前会先启动子进程。这是因为大量调用fork()
的程序立即向子级exec()
提供了一个程序,从而使父级无需与子级共享所有内存。这是更有效的,因为共享内存是写时复制的。
答案 1 :(得分:1)
让我们从这里识别一个主要的误解开始:
从输出中可以看到,fork()返回0时,会打印出Parent的Hello。因此,根据我的理解,Child Process实际上会打印出Parent的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上,作为系统调用调用约定的一部分,返回值存储在寄存器eax
中(对于x64,则为rax
)。
这两个进程之一最终将被调度为由内核运行。在您的情况下,子进程恰巧是第一个被调度的进程。您的用户模式代码从内核模式夺回了控制权,读取了返回值(如果在x86上为eax / rax),该返回值为零,并且没有跳转到标签#1。它打印了Hello from Child!
,然后从函数返回(返回到foo
的调用者,因为孩子得到了父栈的副本)。
父级也发生了同样的情况,只是父级从系统调用返回了非零值并打印了Hello from Parent!
。它按计划运行,并且您的用户模式代码同时从内核控制了系统调用返回的值。
1 这两个输出也可能以某种方式交错,但这与本次讨论不太相关,并且需要了解Linux进程如何执行I / O。