可以确定 fork() 的执行顺序吗?

Posted

技术标签:

【中文标题】可以确定 fork() 的执行顺序吗?【英文标题】:Can the order of execution of fork() be determined? 【发布时间】:2011-10-05 12:48:19 【问题描述】:

我正在编写教科书“操作系统概念第 7 版”的练习,我对 fork() 的工作原理有点困惑。据我了解,fork() 创建了一个与父进程同时运行的子进程。但是,我们如何确切地知道哪个进程首先运行呢?我的意思是执行顺序。

问题 使用fork() 系统调用编写一个C 程序,在子进程中生成斐波那契数列。序列号将在命令行中提供。

这是我的解决方案:

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

void display_fibonacci_sequence( int n ) 
    int i = 0;
    int a = 1;
    int b = 1;
    int value;
    printf( "%d, %d, ", a, b );
    for( ;i < n - 2; ++i ) 
        value = a + b;
        printf( "%d, ", value );
        a = b;
        b = value;
    
    printf( "\n" );


int main( int argc, char** argv ) 
    int n;
    pid_t pid;
    pid = fork();
    if( argc != 2 ) 
        fprintf( stderr, "Invalid arguments" );
        exit( -1 );
    
    n = atoi( argv[1] );

    if( pid < 0 ) 
        fprintf( stderr, "Fork failed" );
        exit( -1 );
    
    else if( pid == 0 ) 
        display_fibonacci_sequence( n );
    
    else  // parent process
        // what do we need to do here? 
    

说实话,我看不出使用fork 和不使用fork 之间有什么区别。另外,如果我想让 parent 进程处理来自用户的输入,让 child 进程处理显示,我该怎么做呢?

【问题讨论】:

哪个进程首先运行(我猜)取决于调度程序。如果您有兴趣,请在 fork() 调用之后获取(高精度)时钟时间并进行比较。 【参考方案1】:

你问了很多问题,我会尽量按方便的顺序回答。

第一个问题

说实话,我看不出使用 fork 和不使用有什么区别 使用叉子。

那是因为这个例子不是一个很好的例子。在你的例子中,父母没有做任何事情,所以叉子是没用的。

第二

else 
    // what do we need to do here? 

您需要wait(2) 才能终止孩子。请务必仔细阅读该页面。

第三

我希望父进程处理来自用户的输入,并让 子进程处理显示

在 fork 之前读取输入并“处理”if (pid == 0) 内的显示

第四

但是,我们如何确切地知道哪个进程首先运行?

很少有程序会关注这一点。你无法知道执行的顺序,它完全取决于环境。 TLPI 是这样说的:

在 fork() 之后,不确定是哪个进程——父进程还是 child—next 可以访问 CPU。在多处理器系统上,它们可能同时访问一个 CPU。

隐式或显式依赖特定应用程序的应用程序 为了达到正确结果的执行顺序是开放的 由于竞争条件而失败

也就是说,操作系统可以让您控制此顺序。例如,Linux 有/proc/sys/kernel/sched_child_runs_first

【讨论】:

【参考方案2】:

我们不知道哪个先运行,父母或孩子。这就是为什么父进程通常必须等待子进程完成,如果它们之间的执行顺序存在一些依赖关系。

在您的具体问题中,没有任何特别的理由使用fork()。你的教授可能只是为了一个简单的例子给你这个。

如果您希望父处理输入而子处理计算,您只需将调用移至处理命令行参数的点下方的fork()。使用与上面相同的基本逻辑,让孩子调用display_fibonacci_sequence,让父母简单地等待

【讨论】:

事实上,我正在教自己不要拘泥于任何特定的课程。尽管如此,书中的练习还是很奇怪。至于你的回答,我不明白为什么家长要等?用户的输入应该首先发生吗?如果父进程等待,那么我们如何为子进程提供值?谢谢。 @Chan,如果“来自用户的输入”是指命令行参数,那么它们甚至在您的程序运行之前就由 shell 处理。孩子获取该值的方式与获取任何其他值的方式完全相同:它在内存中。当您调用fork 时,子进程不会重新开始,而是父进程的克隆,处于完全相同的状态和完全相同的状态变量。所以如果父进程处理argv并将值放入n,那么子进程在运行时与n中的值完全相同。 非常感谢。我现在看到了。【参考方案3】:

您的system scheduler 选择的进程被选择运行,与您操作系统上运行的任何其他应用程序没有什么不同。生成的进程被视为与调度程序分配优先级或队列中的位置或任何实现的任何其他进程一样。

【讨论】:

【参考方案4】:

但是,我们如何确切地知道哪个进程首先运行?我的意思是 执行顺序。

无法保证先跑到哪个。 fork 如果是孩子则返回0,如果是父母则返回孩子的pid。理论上它们可以在多处理器系统上同时运行。如果您真的想确定哪个先运行,您可以在两个进程之间使用共享锁。最先获得锁的可以说是先运行的。

就您的 else 语句中的操作而言。您需要使用waitwaitpid 等待子进程退出。

说实话,我看不出使用 fork 和不使用 fork 之间有什么区别。

不同之处在于您创建了一个子进程。系统上的另一个进程进行计算。对于这个简单的问题,最终用户体验是相同的。但是fork 在编写需要同时处理事物的服务器等系统时就大不相同了。

另外,如果我想让父进程处理用户的输入,让子进程处理显示,我该怎么做呢?

您似乎已经进行了该设置。父进程只需要等待子进程完成。子进程将printf 将结果发送到终端。并且父进程当前从命令行获取用户输入。

【讨论】:

【参考方案5】:

虽然您无法控制在 fork 之后首先安排哪个进程(父进程或子进程)(实际上在 SMP/多核上它可能是both!)有很多方法可以同步这两者进程,让一个等待直到另一个到达某个点,然后再执行任何重要的操作。一种经典的、极其便携的方法如下:

    fork 之前,调用pipe 创建管道。 紧跟在fork之后,想要等待的进程应该关闭管道的写端并在管道的读端调用read。 其他进程应立即关闭管道的读取端,并等待关闭管道的写入端,直到它准备好让其他进程运行。 (read 会在其他进程中返回 0)

【讨论】:

以上是关于可以确定 fork() 的执行顺序吗?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以确定 GHUnit 测试方法的执行顺序?

(疑问)进程控制---vfork 函数

确定 Spock 测试的执行顺序

sql语句中的关键字有优先级,以及执行顺序吗?

多个 fs.write 追加到同一个文件可以保证执行顺序吗?

确定 Android 应用程序中语句的执行顺序?