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

int main() {
    
    pid_t pid;
    pid = fork();

    fork();
    if (pid > 0) {
        printf("Hello, World!\n");
    }

    return EXIT_SUCCESS;
}

When the code above is run it prints the following output, which is expected:

Hello, World!
Hello, World!

But sometimes I get the following output after executing the code again:

Hello, World!

Why does the code sometimes print the message only once?

This only happens when the program is run in the terminal of an IDE, or in an environment like onlinegdb.

When the program is started from an interactive shell, then always both expected lines of "Hello, World!" are printed.

2
Comments are not for extended discussion; this conversation has been moved to chat. - Samuel Liew

2 Answers

0
votes

TL;DR race condition between forking a child and exiting

Depending on whether the child or the parent runs first after the (second) fork, it is possible for the parent to print the message and exit and the IDE to detect that it has exited and shut everything down before the child ever gets to run and print it's message.

This doesn't happen when running from bash, becasue even if it did, the child would still be able to output its messgage. In theory that might happen after the shell prints the next prompt, but that would seem extremely unlikely.

The important thing to remeber is that your program is always being run by something (a shell or and IDE or whatever), and that something will wait for it to exit, but WILL NOT wait for any children you might fork. So if you want to be sure your children complete, you need to wait for them before you exit.

0
votes

The observed behaviour is caused by shell terminating after it sees the original process terminate.

The clue is vxs8122's observation that

when I [...] executed the code directly on the OS terminal, I stopped having that issue

When the process is started from an interactive shell, the interactive shell keeps running after the original process terminates. The other three forked processes can run to their normal end, so that two of them print "Hello world".

When the process is started by a IDE, typically the IDE creates a shell instance to execute the process (or the IDE performs equivalent actions like a shell by itself).

A shell puts the child process into a new process group, and makes it the process group leader. The forked processes inherit the process group. The shell waits for its original child to terminate, then it terminates itself. Before terminating, the shell sends SIGHUP to the process group, to make sure that no orphaned processes keep hanging around. This signal may reach any of the up to three forked processes, if they haven’t already terminated by then.

SIGHUP isn’t caught in the example program, and its standard action is to terminate the process. So a „printing“ process may be killed early by the terminating shell. In this case the output isn’t printed.

If you ignore SIGHUP in the program, early in main(), then always both lines are printed.

signal(SIGHUP, SIG_IGN);

You can also try this version, to verify that the child processes receive SIGHUPs, depending on your running environment.

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

void handler(int sig)
{
    puts("got SIGHUP");
    exit(EXIT_SUCCESS);
}

int main() {
    
    signal(SIGHUP, handler);

    pid_t pid;
    pid = fork();

    fork();
    if (pid > 0) {
        printf("Hello, World!\n");
    }

    return EXIT_SUCCESS;
}