该程序中涉及 fork() 系统调用的控制流如何?
How is the flow of control in this program involving fork() system call?
据我了解 fork()
系统调用
fork系统调用用于创建一个新进程,称为child进程,与parent进程
并发运行
创建新的child进程后,两个进程将执行fork()系统调用后的下一条指令
fork()
returns 0 到 child 进程
fork()
returns新建child进程到parent进程的进程ID(正值)
fork()
returns 负值如果 child 进程创建失败
在这段代码中
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!
child进程是在主进程的foo函数的if-else
条件下创建的child进程。
那么 child 进程是从哪里(哪条指令)开始执行的?
从输出中可以看出,Hello from Parent
在fork()
returns0
时打印出来。所以根据我的理解 Hello from Parent
实际上是由 Child Process
打印的
fork()
向 parent 进程返回一个正值,并且 parent 进程打印 Hello from Child
。我对此的理解正确吗?
child 进程是从哪条指令开始执行的? fork()
的函数调用是在 if-else
的条件部分中给出的。所以 child 应该在 if-else
之后开始执行,但事实并非如此?
子进程是并行执行的第二个进程。你可能很容易得到
Hello from Child!
Hello from Parent!
例如,如果您打开终端 window,然后启动 firefox &
,它“首先”运行终端 window 或浏览器 window ?两者同时运行
事实上,Linux 在重新启动父进程之前稍稍启动了子进程。这是因为大量调用 fork()
的程序会立即让子程序 exec()
成为一个程序,从而使父程序无需与子程序共享其所有内存。这样效率更高,因为共享内存是copy-on-write.
让我们首先确定这里的一个主要误解:
As it can be observed from the output, Hello from Parent is printed when fork() returns 0. So from my understanding Hello from Parent was actually printed by the Child Process
child 和 parent 是两个独立的进程 运行ning 并发。这两个输出的顺序不是 well-defined,会根据您的内核和其他时序考虑因素而有所不同,并且与您的代码包含按您所写的 if/else 块的事实无关.1
让我们将您的代码重写为抽象意义上的 "instructions" 的线性流:
0: Function foo():
1: Invoke system call fork(), no arguments, store result to
2: If 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:
,就会调用系统调用,将控制权转移到内核。内核复制进程,将child的PID放入parent进程中fork
的return值,将0
放入[= child 中 fork
的值 58=]。在 x86 上,return 值作为系统调用约定的一部分存储在寄存器 eax
(对于 x64 为 rax
)。
这两个进程之一最终将被内核调度到 运行。在您的情况下,child 进程恰好是第一个被安排的。您的 user-mode 代码从内核模式取回控制权,读取为零的 return 值(如果在 x86 上则为 eax/rax),并且没有跳转到标签 #1。它打印 Hello from Child!
,然后从函数 returned(到 foo
的调用者,因为 child 得到了 parent 的堆栈副本).
除了 parent 从系统调用返回一个 non-zero 值并打印 Hello from Parent!
外,parent 也发生了同样的情况。它被安排到 运行,并且您的 user-mode 代码在同一时间点从内核获取控制权,只是系统调用使用了不同的值 return。
1 两个输出也有可能以某种方式交错,但这与本次讨论无关,需要了解 Linux 进程如何执行I/O.
据我了解 fork()
系统调用
fork系统调用用于创建一个新进程,称为child进程,与parent进程
并发运行创建新的child进程后,两个进程将执行fork()系统调用后的下一条指令
fork()
returns 0 到 child 进程
fork()
returns新建child进程到parent进程的进程ID(正值)
fork()
returns 负值如果 child 进程创建失败
在这段代码中
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!
child进程是在主进程的foo函数的if-else
条件下创建的child进程。
那么 child 进程是从哪里(哪条指令)开始执行的?
从输出中可以看出,Hello from Parent
在fork()
returns0
时打印出来。所以根据我的理解 Hello from Parent
实际上是由 Child Process
fork()
向 parent 进程返回一个正值,并且 parent 进程打印 Hello from Child
。我对此的理解正确吗?
child 进程是从哪条指令开始执行的? fork()
的函数调用是在 if-else
的条件部分中给出的。所以 child 应该在 if-else
之后开始执行,但事实并非如此?
子进程是并行执行的第二个进程。你可能很容易得到
Hello from Child!
Hello from Parent!
例如,如果您打开终端 window,然后启动 firefox &
,它“首先”运行终端 window 或浏览器 window ?两者同时运行
事实上,Linux 在重新启动父进程之前稍稍启动了子进程。这是因为大量调用 fork()
的程序会立即让子程序 exec()
成为一个程序,从而使父程序无需与子程序共享其所有内存。这样效率更高,因为共享内存是copy-on-write.
让我们首先确定这里的一个主要误解:
As it can be observed from the output, Hello from Parent is printed when fork() returns 0. So from my understanding Hello from Parent was actually printed by the Child Process
child 和 parent 是两个独立的进程 运行ning 并发。这两个输出的顺序不是 well-defined,会根据您的内核和其他时序考虑因素而有所不同,并且与您的代码包含按您所写的 if/else 块的事实无关.1
让我们将您的代码重写为抽象意义上的 "instructions" 的线性流:
0: Function foo():
1: Invoke system call fork(), no arguments, store result to
2: If 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:
,就会调用系统调用,将控制权转移到内核。内核复制进程,将child的PID放入parent进程中fork
的return值,将0
放入[= child 中 fork
的值 58=]。在 x86 上,return 值作为系统调用约定的一部分存储在寄存器 eax
(对于 x64 为 rax
)。
这两个进程之一最终将被内核调度到 运行。在您的情况下,child 进程恰好是第一个被安排的。您的 user-mode 代码从内核模式取回控制权,读取为零的 return 值(如果在 x86 上则为 eax/rax),并且没有跳转到标签 #1。它打印 Hello from Child!
,然后从函数 returned(到 foo
的调用者,因为 child 得到了 parent 的堆栈副本).
除了 parent 从系统调用返回一个 non-zero 值并打印 Hello from Parent!
外,parent 也发生了同样的情况。它被安排到 运行,并且您的 user-mode 代码在同一时间点从内核获取控制权,只是系统调用使用了不同的值 return。
1 两个输出也有可能以某种方式交错,但这与本次讨论无关,需要了解 Linux 进程如何执行I/O.