流程执行:为什么fork()的输出顺序如此?

时间:2018-10-13 18:07:45

标签: c linux operating-system printf fork

我正在做一些操作系统101作业,并通过一些C代码进行挖掘。 我是C和Linux的新手,所以我有一个可能不寻常的问题。我必须检查一个C程序来确定它启动了多少个进程。因此,我读了很多书,并修改了原始代码以回答所有问题。

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

int main (void) {
   printf("Start ID %d\n\n", getpid());

   printf("1.fork() from ");   
   printf("ID: %d\n", getpid());
   fflush(stdout);
   fork();

   printf("2.fork() from ");
   printf("ID: %d my parent is ID %d\n", getpid(), getppid());   
   fflush(stdout);
   fork();

   printf("3.fork() from ");
   printf("ID: %d my parent is ID %d\n", getpid(), getppid());  
   fflush(stdout);
   fork();

   sleep(2);   
   printf("%d finished. Good Night!\n", getpid());
   return EXIT_SUCCESS;
}

有一件事我不明白。为什么在分叉之前,printf()的输出按此顺序:

1.fork() from ID: 3124
2.fork() from ID: 3124 my parent is ID 2215
3.fork() from ID: 3124 my parent is ID 2215
3.fork() from ID: 3126 my parent is ID 3124
2.fork() from ID: 3125 my parent is ID 3124
3.fork() from ID: 3125 my parent is ID 3124
3.fork() from ID: 3129 my parent is ID 3125

我希望

1.fork() from ID: 3124
2.fork() from ID: 3124 my parent is ID 2215
3.fork() from ID: 3124 my parent is ID 2215
2.fork() from ID: 3125 my parent is ID 3124
3.fork() from ID: 3125 my parent is ID 3124
3.fork() from ID: 3126 my parent is ID 3124 
3.fork() from ID: 3125 my parent is ID 3124
3.fork() from ID: 3129 my parent is ID 3125

因为PID 3124用第一个fork()启动PID 3125,另外两个子进程都用第二个fork(),依此类推。 CPU是否按照创建顺序执行进程?这不是我的家庭作业的一部分,但我对此仍然感到好奇。

2 个答案:

答案 0 :(得分:0)

printf语句的输出可能因运行而异,因为一个分支过程的完成速度可能比另一个过程快。在这些情况下,绘制叉形树图以确定所有可能的情况将很有帮助。在您的情况下,您只能保证第一个打印语句将首先打印,第二个打印语句将在同一fork中的第三个打印语句之前打印,第二个打印语句将在第二个派生子级中的第三个打印语句之前打印。这给出了几种可能的输出。从技术上讲,一个进程可能不会在2秒内完成,并可能在其中一个finish语句之后打印其输出,尽管这不太可能。

P.S。最好将fork包装在if中以检查系统错误,但是通常可以正常工作。

答案 1 :(得分:0)

您不能真正确定将首先执行哪个进程。就像 HackerBoss 所说的,printf也可以影响此顺序。

想象一下您的主程序pid(3124)生成子3125。生成子代后,父子都需要调用以下指令:

printf("2.fork() from ");

这时有两个方向:

  1. 脂肪3124调用printf
  2. 孩子3125调用printf

由于printf要求I/O scheduling,因此它取决于process priorityresource state(可能已经有另一个进程正在使用该资源,因此将其变成{{ 1}})。

因此,看起来在您的程序中,父亲busy resource首先获得了对该资源的访问权,并继续执行直到下一个派生分支,在此分支中生成了子3124

这时有一个相同的问题:我应该朝哪个方向前进?下一条指令是:

3126

方向是:

  1. 脂肪printf("3.fork() from "); 调用3124
  2. 孩子printf调用3126

从您的程序看来,调用它的第一个过程是子printf

因此,实际上3126不能向您保证流程的生成顺序。由于printf的工作方式是透明的,因此更好的方法是通过将I/O scheduling包裹在fork语句中,将值存储在每个进程不同的特定地址中:

if

这样,您可以更好地了解pid=fork(); if (pid == 0) { //child process } else { //father process } 的操作,因为实际上process scheduler可能会先启动一个孩子,因此有很多调度算法。此时,您正在运行的process scheduler还会影响流程的执行顺序,具体取决于所使用的算法。