fork() 乍一看可能有点令人困惑,但实际上它非常简单。它所做的是将当前进程复制到另一个内存位置的新进程中(复制所有内容、其数据、代码、当前指令……)。
所以我们从一个 pid = 5079 的进程开始,当我们进行 fork 调用时,创建了一个 pid = 5080 的子进程,它的代码与父进程相同。
// Parent Process // // Child Process //
#include<stdio.h> #include<stdio.h>
#include<unistd.h> #include<unistd.h>
#include<stdlib.h> #include<stdlib.h>
int main() int main()
{ {
printf("\... printf("\...
pid_t pid1=fork(); pid_t pid1=fork();
printf("\nPI... //Next line// printf("\nPI... //Next line//
pid_t pid2=fork(); pid_t pid2=fork();
printf("\nPID:=... printf("\nPID:=...
pid_t pid3=fork(); pid_t pid3=fork();
printf("\nPID:=... printf("\nPID:=...
return 0; return 0;
} }
在继续看代码之前,fork调用的返回值如下:在调用fork()的进程内部,返回值为子进程的pid(父进程中pid1变量=5080) ,而在子进程内部,输出为0(子进程中的pid1变量=0)。
所以fork之后的print语句会被父进程和子进程执行,getpid()值不同,pid1值不同,父进程getpid()=5079,pid1=子进程的pid=5080(你可以在输出的第三行看到这一点)。子进程会自己做打印语句,getpid() = 5080 and pid1 = 0,你可以在输出的第8行看到这个,但是为什么是第8行!!!
操作系统调度进程,也就是说,它决定 CPU 将在哪个进程上工作以及工作多长时间。所以看起来操作系统决定父进程(pid = 5079)应该运行一段时间,而让子进程(pid = 5080)等待CPU执行它的指令。
所以进程 5079 继续下一个分叉,创建一个 pid = 5081 的新子进程。然后它在第三行打印我们期望的内容,然后它继续到最后一个分叉创建进程 5082,打印我们想要的期望在第四行,然后终止(进程 5079 终止,留下 5080,5081,5082 等待,并且被操作系统采用,它们需要有父级,但这对输出并不重要)。
现在 5079 终止了,我们有 3 个进程在内存中等待 CPU 运行它们。操作系统必须决定运行哪个进程,而且它似乎已经选择了最接近终止的进程,也就是进程 5082,我们来看看每个进程的剩余指令:
// process 5082 // // process 5081 // // process 5080 //
printf("\nP... printf("\nP... printf("\nP...
return 0; pid_t pid3=fork(); pid_t pid2=fork();
printf("\nP... printf("\nP...
return 0; pid_t pid3=fork();
printf("\nP...
return 0;
为什么这是剩下的代码?像我们之前看到的那样,在其他进程中由 fork 创建的任何进程都将在该 fork 语句之后开始执行。所以进程 5082 打印了第 5 行然后终止(它的值 pid3 = 0 因为它是 5079 的孩子)。终止 5082 后,5081 占用 CPU,并打印第 6 行,然后创建进程 5085,正如我们在第 6 行中看到的那样(为什么不按顺序排列 5083?也许操作系统在执行代码期间创建了一些进程)。
在打印第 6 行后,进程 5081 已终止。现在我们在内存中有 5080 和 5085。您现在应该能够遵循该模式,选择运行 5080,创建 5086 和 5087 然后终止。然后 5085 运行,然后是 5087,它最后只有 print 语句,然后都终止了,我们只剩下 5086 打印了,最后一个 fork 创建了 5088,然后像 5088 打印后一样终止。
操作系统是一个令人着迷的领域,它的乐趣超越了系统调用,如果你对此感兴趣,我会推荐这本书,这是我大学时学习的:
https://www.amazon.com/Operating-System-Concepts-Abraham-Silberschatz/dp/0470128720