Skip to content

Mandatory task 1 ING2504 Fall 2025

You MUST watch this lecture BEFORE you start doing this task!

When you have completed all three parts of this mandatory task, show/demonstrate your solution to Erik

Part 1: Understanding fork()

Goal

Write a C program that creates exactly four processes in total using fork(). The original process (the root parent) should create a child; both the parent and that child should each create one additional child, resulting in this two-level tree:

Level 0: P0 (original parent)
 ├─ Level 1: C1 (child of P0)
 │   └─ Level 2: C3 (child of C1)
 └─ Level 1: C2 (child of P0)

Create a file four.c with the following starter code, and you only have to add code where it says YOUR CODE HERE:

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

int main(void) {
    pid_t pid1, pid2;
    int depth = 0;

    // this is a statement to make stdout unbuffered so each
    // process prints its line immediately, just leave as is
    setvbuf(stdout, NULL, _IONBF, 0);

    // TODO 1: First fork. After this, you should have 2 processes:
    // - The original parent (depth 0)
    // - The first child (depth 1)
    // YOUR CODE HERE (fork)
    if (pid1 < 0) {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    // YOUR CODE HERE (if test)
        // We are the child created by the first fork
        depth = 1;    

    // TODO 2: Second fork. Both current processes should execute
    // exactly one more fork, creating one new child each.
    // YOUR CODE HERE (fork)
    if (pid2 < 0) {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    // YOUR CODE HERE (if test)
        // We are the child created by the second fork
        depth++; // child is one level deeper than its parent

    // TODO 3: Print exactly one line per process:
    // Format: depth=<d> pid=<PID> ppid=<PPID>
    // add zero, one or two tabs (\t) dependent on depth
    if (depth == 0) {
    // YOUR CODE HERE (printf)
    } else if (depth == 1) {
    // YOUR CODE HERE (SAME printf but with \t)
    } else if (depth == 2) {
    // YOUR CODE HERE (SAME printf but with \t\t)
    }

    // Reap any children (if we have any) to avoid zombies, leave as is
    while (wait(NULL) > 0) {
        // loop until no more children
    }

    return 0;
}

Running the program should output something like this:

$ ./four
depth=0 pid=3859940 ppid=2431311
  depth=1 pid=3859942 ppid=3859940
  depth=1 pid=3859941 ppid=3859940
    depth=2 pid=3859943 ppid=3859941

Hints

  • fork() returns 0 in the child, and the child’s PID in the parent. Use this to decide paths.
  • Use getpid() and getppid() to print IDs.

Part 2: Understand fork()-exec() combination and simple synchronization

(a) Write a C-program mycow.c

  1. Start from p3.c
  2. Instead of executing the wordcount program, the child processes should execute cowsay (note: you might have to install the cowsay program) with arguments provided on the command line
  3. The program should loop over the command line arguments and fork a child process for each of them and execute cowsay using the system call execvp in the same way as p3.c.
  4. The program should output in the same order as the command line arguments are provided, and only one cowsay should be executed at a time (in other words: use waitpid for this simple synchronization).

Here as an example of how mycow should run:

$ ./mycow "Let's moo-ve!" "Holy cow!" "Deja-moo" "Bullshit"
 _______________
< Let's moo-ve! >
 ---------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
 ___________
< Holy cow! >
 -----------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
 __________
< Deja-moo >
 ----------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
 __________
< Bullshit >
 ----------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
$

If you don't synchronize correctly with waitpid, you might get something like this:

$ ./mycow "Let's moo-ve!" "Holy cow!" "Deja-moo" "Bullshit"
 _______________
 __________
 ___________
< Let's moo-ve! >
< Deja-moo >
< Holy cow! >
 ---------------
 ----------
 -----------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
 __________
< Bullshit >
 ----------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
$

(b) Understanding simple parallelization from the command line

Run your cowsay program ten times in sequence:

for i in {1..10}; do ./mycow "Let's moo-ve!" "Holy cow!" "Deja-moo" "Bullshit" ; done

Scroll up to see that the output is correct (no lines are mixed/interleaved) all the time.

Replace the last ;with &. Run it again.

  1. Is the output still correct?
  2. What is different in the way the program is run now?
  3. (Optional difficult task) How can you fix this?