Prof. Alessandro Armando

View on GitHub

Calcolatori Elettronici - Linux Processes

1. Introduction

Each process in a Linux system is identified by its unique process ID, sometimes referred to as pid.

Process IDs are 16-bit numbers that are assigned sequentially by Linux as new processes are created.

Every process also has a parent process (except the special init process). Thus, you can think of the processes on a Linux system as arranged in a tree, with the init process at its root.

The parent process ID, or ppid, is simply the process ID of the process’s parent.

When referring to process IDs in a C or C++ program, always use the pid_t typedef, which is defined in <sys/types.h>.

A program can obtain the process ID of the process it’s running in with the getpid() system call, and it can obtain the process ID of its parent process with the getppid() system call.

#include <stdio.h>
#include <unistd.h>
int main () {
printf("The process ID is %d\n", (int) getpid ());
printf("The parent process ID is %d\n", (int) getppid ());
return 0;
}

2. Using fork

Linux provides one function, fork, that makes a child process that is an exact copy of its parent process.

When a program calls fork, a duplicate process, called the child process, is created:

So how do the two processes differ? First, the child process is a new process and therefore has a new process ID, distinct from its parent’s process ID.

One way for a program to distinguish whether it’s in the parent process or the child process is to call getpid. However, the fork function provides different return values to the parent and child processes:

Important:

Because no process ever has a process ID of zero, this makes it easy for the program whether it is now running as the parent or the child process.

// 03_fork.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main() {
    int child_status;
    pid_t child_pid;
    printf("The main program has the PID %d.\n\n", getpid());
    child_pid = fork();
    /*
     * fork() creates a new process. 
     * The return value is 0 in the child and the pid of the child in the parent
     */
 
    if(child_pid != 0) {
        printf("This is the parent process with PID %d.\n", getpid());
        printf("The child process has the PID %d.\n", child_pid);
        wait(&child_status); // waits for the first child to die (any one!)
    }
    else {
        printf("This is the child process with PID %d.\n", getpid());
        printf("Its parent process is with PID %d.\n", getppid());
    }

	return 0;
}


Exercises

  1. Here is a simple exercise. Check your solution here.
  2. Here is a slightly mode difficult exercise. Check your solution here.

Copile and execute (multiple times) the following program. What do you observe?

// 03_fork_arrows.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define N 1000

int main() {

    int child_status;
    pid_t child_pid;
    int i;

    printf("The main program has the PID %d.\n\n", getpid());
    
    child_pid = fork();
 
    if(child_pid != 0) {
        printf("This is the parent process with PID %d.\n", getpid());
        printf("The child process has the PID %d.\n", child_pid);
	for(i=0;i<N;i++) {
	  fputc ('-', stdout);
	  fputc ('>', stdout);
	  fputc ('\n', stdout);
	}
    }
    else {
        printf("This is the child process with PID %d.\n", getpid());
        printf("Its parent process is with PID %d.\n", getppid());
	for(i=0;i<N;i++) {
	  fputc ('<', stdout);
	  fputc ('=', stdout);
	  fputc ('\n', stdout);
	}
    }

	return 0;
}

3. Using the exec family

Linux provides another set of functions, the exec family, that causes a particular process to cease being an instance of one program and to instead become an instance of another program from the beginning (assuming that the exec call doesn’t encounter an error).

The exec functions replace the program running in a process with another program.

To spawn a new process, you first use fork to make a copy of the current process, then you use exec to transform one of these processes into an instance of the program you want to spawn.

4. Using fork and exec together

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

int spawn(char* program_name, char** arg_list) {
    pid_t child_pid;
    child_pid = fork();
    if(child_pid != 0) {
        return child_pid; // if I am the parent, return the child pid
    }
    else {
        execvp(program_name, arg_list);
        /* The execvp will never return unless error occurs */
        fprintf(stderr, "An error occured when invoking execvp.\n");
        exit(EX_OSERR);
    }
}

int main() {
    int child_status, child_pid;

    char* arg_list[] = {
        "ls",
        "-l",
        "/",
        NULL
    };
    child_pid = spawn(arg_list[0], arg_list);
    waitpid(child_pid, &child_status, 0); // wait for the child whose PID is equal to child_pid
    /*
     * if you have only one child you can use:
     * wait (&child_status);
     */
    if(WIFEXITED(child_status)) {
        printf("The child process exited normally with exit code %d.\n", WEXITSTATUS(child_status));
    }
    else {
        printf("The child process exited abnormally.\n");
    }
    printf("Done with the main program.\n");
    return 0;
}