Unix & Linux
solaris c system-calls exec fork
Updated Fri, 20 May 2022 04:05:56 GMT

Parent process always printing output after child


Consider the following code running under 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);
    }
 }

Whenever I run it, the "Writing from parent" line is always output last. I would not be surprised with that result if my school task wasn't to use wait(2) in order to print that line only after the child process has finished. Why does this happen and how to ensure that this line is printed before the child process executes cat (or the order is at least undefined), so I can safely use wait(2) or waitpid(2) to tackle that?




Solution

As @AndrewHenle commented, depending on the system to schedule your processes in a specific sequence is unsafe and unjustified. Even when the scheduling appears to be consistent (as in your case), there is nothing preventing the operating system's implementers from altering the scheduler's behavior.

If order of operations between processes/threads is relevant, some form of communication is necessary.

For your scenario, a simple blocking read can do the job. Create a pipe before the fork. Then write to the pipe from the parent only after the parent has printed its message. Meanwhile, the child's attempt to read from the pipe will block its execution until the parent writes.

Neglecting error handling and unused pipe file descriptors in each process (which are usually explicitly closed after forking):

#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);
    }

You should probably use wait in the parent, so that if it is run from a terminal, the child's output and shell prompt are not interleaved.