父进程总是在子进程之后打印输出

父进程总是在子进程之后打印输出

考虑在 Solaris 11.3 下运行的以下代码:

int main(void) {
    pid_t pid = fork();
    if (pid > 0) {
        printf("[%ld]: Writing from parent process\n", getpid());
    }
    if (pid == 0) {
        execl("/usr/bin/cat", "/usr/bin/cat", "file.c", (char *) 0);
        perror("exec failed");
        exit(1);
    }
 }

每当我运行它时,“从父级写入”行总是最后输出。如果我的学校任务不是使用 wait(2) 以便仅在子进程完成后打印该行,我不会对此结果感到惊讶。为什么会发生这种情况以及如何确保在子进程执行 cat 之前打印此行(或者顺序至少未定义),以便我可以安全地使用 wait(2) 或 waitpid(2) 来解决这个问题?

答案1

正如 @AndrewHenle 评论的那样,依赖系统以特定顺序安排进程是不安全且不合理的。即使调度看起来是一致的(如您的情况),也没有什么可以阻止操作系统的实现者改变调度程序的行为。

如果进程/线程之间的操作顺序相关,则需要某种形式的通信。

对于您的场景,简单的阻塞读取就可以完成这项工作。在叉子之前创建一个管道。然后仅在父级打印其消息后才从父级写入管道。同时,子进程尝试从管道中读取数据将阻止其执行,直到父进程写入为止。

忽略每个进程中的错误处理和未使用的管道文件描述符(通常在分叉后显式关闭):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/wait.h>

int main(void) {

    char buf[1];
    int pipe_fds[2];
    pipe(pipe_fds);

    pid_t pid = fork();
    if (pid > 0) {
        printf("[%ld]: Writing from parent process\n", getpid());
        write(pipe_fds[1], "_", 1);
        wait(NULL);
    }
    if (pid == 0) {
        read (pipe_fds[0], buf, 1);
        execl("/usr/bin/cat", "/usr/bin/cat", "file.c", (char *) 0);
        perror("exec failed");
        exit(1);
    }

您可能应该在父级中使用 wait,这样如果从终端运行它,子级的输出和 shell 提示符就不会交错。

答案2

从内核的角度考虑这一点:它只是通过设置子进程来破坏各种缓存,接下来运行该子进程可能会重用一些新的缓存内容。同时,父进程可能会被调度到另一个 CPU 上。

答案3

虽然原则上两个进程可以并行运行,但父进程已经在 CPU 上处于活动状态,而子进程必须先创建exec新进程才能创建其输出。父进程很可能在子进程替换其进程之前完成。

相关内容