05 - Process API - Part II

Outline

  1. Process synchronization using wait()
    • Allows for a process to wait until the status of a child has changed
    • Can do more than just that
    • You read the exit code of the child (find out what happened to the child, i.e, got killed, exit gracefully, etc.)
  2. Zombie processes
    • If the child finishes first before the parent calls wait() the child becomes a zombie (just hangs out)
  3. Zombie process prevention

Intro

Process synchronization

The program below lacks synchronization between the child and the parent.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
    printf("Hello!! My ID is %d, my parent ID is %d.\n",getpid(), getppid());
    pid_t pid = fork();
    printf("Bye!! My ID is %d, my parent ID is %d.\n",getpid(), getppid());
    return 0;
}
davidkebo@linux:~/code/week4/forks$ ./a.out
Hello!! My ID is 75189, my parent ID is 74907.
Bye!! My ID is 75189, my parent ID is 74907.
Bye!! My ID is 75190, my parent ID is 75189.

Pasted image 20260206143704.png|150

Notes


Every process must be "reaped" in Unix, otherwise it becomes a zombie and its task_Struct (a Linux kernel structure) hands around, which is wasted kernel space

Reaping: need to retrieve the status of a process

A process can be reaped only by its parent! So what happens when a process parent dies. Who reaps it?

Answer: init/systemd

The init process:


Process synchronization - I

The waitpid function makes a process wait for a child process.

pid_t waitpid(pid_t pid, int *status, int options)

The three arguments are:

A simpler way to wait for any child process is to use wait

pid_t wait(int *status) = waitpid(-1, \&wstatus, 0);

Pasted image 20260209140342.png|400

Notes:

Process synchronization: example 1

int value = 5;
int pid = fork();
if (!pid) {
    value += 5;
    cout << "Child has value=" << value << endl;
} else {
    value += 10;
    cout << "Parent has value=" << value << endl;
}
prompt> ./a.out
Parent has value=?
Child has value=?

int value = 5, status;
int pid = fork();
if (!pid){
    value += 5;
    cout<< "Child has value="<<value<kend";
    exit(100);    // trying to tell something to the parent
}else{
    waitpid (pid, &status, 0); // wait for the child (parent listening)
    value += 10;
    cout << "Child terminated, status="
        <<WEXITSTATUS(status) << endl;
    cout<< "Parent has value="<<value<<endl;
}
prompt> ./a.out
Child has value=?
Child terminated, status=?
Parent has value=?

Notes:

Process synchronization: example 1

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void wait_demo() {
    int child_status;
    if (fork() == 0) {
        printf("HC: hello from child\n");
    } else {
        printf("HP: hello from parent\n");
        wait(&child_status);
        printf("CT: child has terminated\n");
    }
    printf("Bye\n");
}

Notes:

Zombie processes

  1. After a fork() the child process executes independently of the parent.
  2. If the parent does not wait for the child to terminate and executes subsequent tasks, then at the termination of the child, the exit status is not captured by the parent.
    • A PCB (Process Control Block) entry remains in the process table even after the termination of the child.
    • The orphaned child becomes a zombie process.
  3. Multiple zombies can result in memory leaks, because the PCBs consume memory and are not deallocated.

Notes:

Pasted image 20260209142216.png|350

Zombie processes: example 1

Example: the fork bomb

A program creates infinite zombie processes because the parent does not wait for the child process.

#include <unistd.h>
int main()
{
    while(1)
        fork();
    return 0;
}

Zombie processes: example 2

int main() {
    int pid = fork();
    if (pid == 0) {
        for (int i=0; i<20; i++)
            printf("I am Child\n");
    } else {
        printf("I am Parent\n");
        while(1);
    }
}
davidkebo@linux:~/code/week4/zombie\$ gcc zombie1.c
davidkebo@linux:~/code/week4/zombie\$ ./a.out
I am Parent
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
 - davidkebo@linux:  $ ps aux | grep a.out  davidke+ 7883599.40.02496576pts/4R+17:400:13./a.out  davidke+ 788360.00.000pts/4Z+17:400:00 [a.out] <defunct>  davidke+ 788800.00.081682568pts/5 S+17:410:00 grep --color=auto a.out 

Notes:

Zombie prevention methods

How to prevent zombie processes?

a) Using the wait() system call

Notes:

Zombie prevention using wait()

int main() {
    int pid = fork();
    if (pid == 0) {
        for (int i=0; i<20; i++)
        printf("I am Child\n");
    } else {
        wait(NULL);    // NEW LINE
        printf("I am Parent\n");
        while(1);
    }
}
davidkebo@linux:~/code/week4/zombie\$ gcc zombie1.c
davidkebo@linux:~/code/week4/zombie\$ ./a.out
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Parent
davidkebo@linux:~$ ps aux | grep a.out
davidke+ 78835 99.4 0.0 2496 576 pts/4 R+ 17:40 0:13 ./a.out
davidke+ 78836 0.0 0.0 0 0 pts/4 Z+ 17:40 0:00 [a.out] <defunct>
davidke+ 78880 0.0 0.0 8168 2568 pts/5 S+ 17:41 0:00 grep --color=auto a.out

Zombie prevention methods

How to prevent zombie processes?

Notes:

Zombie prevention using wait()

int main() {
    int pid = fork();
    if (pid == 0) {
        for (int i=0; i<20; i++)
            printf("I a m Child\n");
    }else {
        wait(NULL);
        printf("I am Parent\n");
        while(1);
    }
}

Zombie prevention using SIGCHLD & SIG_IGN I

int main() {
    int pid = fork();
    if (pid == 0) {
        for (int i=0; i<20; i++)
            printf("l am Child\n");
    } else {
        signal(SIGCHLD, SIG_IGN);
        printf("I am Parent\n");
        while(1);
    }
}

Zombie prevention using SIGCHLD II

void func(int signum) {
    wait(NULL);
}

int main() {
    int pid = fork();
    
    if (pid == 0) {
        for (int i=0; i<20; i++)
            printf("I am Child\n");
    } else {
        signal(SIGCHLD, func);
            printf("I am Parent\n");
            while(1);
    }
}

Killing zombie processes

The state of a terminated child when the parent is still running w/o calling wait()

int main () {
    if (fork()){ // parent
        while (true){//infinite loop
            sleep (1);
        }
    }else{// child
        cout<<"Child about to exit"<<endl;
    }
}

Pasted image 20260211142841.png

Who reaps a child when the parent is killed?

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
int main() {
    int pid = fork();
    if (pid == 0) {
        printf("parent pid = %d.\n", getpid());
        pid = fork();
        if (pid == 0) {
            printf("child pid = %d.\n", getpid());
            pause();
        } else {
            pause();
        }
    } else {
        printf("grandparent pid = %d.\n", getpid());
        pause();
    }
    // grandparent
    return 0;
}

Notes: