Skip to main content
  1. Posts/

OSTEP Chapter 5: Interlude - Process API Review

·777 words·4 mins

Heptabase Note

Question: How do we create and manage processes?

Now that we have the concept of process, we can further think about how to create and manage them.

Process APIs #

Operating systems provide several system calls related to process, and we will discuss only a few of them below.

The fork() system call #

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

int main(int argc, char *argv[])
{
    printf("hello world (pid:%d)\n", (int) getpid());
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // child (new process)
        printf("hello, I am child (pid:%d)\n", (int) getpid());
    } else {
        // parent goes down this path (original process)
        printf("hello, I am parent of %d (pid:%d)\n",
	       rc, (int) getpid());
    }
    return 0;
}
// Result

prompt> ./p1
hello world (pid:29146)
hello, I am parent of 29147 (pid:29146)
hello, I am child (pid:29147)
prompt>

// OR 

prompt> ./p1
hello world (pid:29146)
hello, I am child (pid:29147)
hello, I am parent of 29147 (pid:29146)
prompt>

We can notice a few characteristics of fork():

  1. The new process is almost identical to the parent. This can be seen from the fact that they run almost the same code, where the child process starts executing from an if statement with the rc value being zero.

  2. When two processes run at the same time, we might get two different results. (This will be discussed in more depth in the later scheduler section)

The wait() system call #

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

int main(int argc, char *argv[])
{
    printf("hello world (pid:%d)\n", (int) getpid());
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // child (new process)
        printf("hello, I am child (pid:%d)\n", (int) getpid());
	sleep(1);
    } else {
        // parent goes down this path (original process)
        int wc = wait(NULL);
        printf("hello, I am parent of %d (wc:%d) (pid:%d)\n",
	       rc, wc, (int) getpid());
    }
    return 0;
}
// Result

prompt> ./p2
hello world (pid:29266)
hello, I am child (pid:29267)
hello, I am parent of 29267 (rc_wait:29267) (pid:29266)
prompt>

If you do not want to leave the descision of execution order to the scheduler , then we need other ways to achieve this purpose, hence we have wait().

Using wait(), the parent process can wait for the child process to finish executing before proceeding.

The exec() system call #

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
	// child: redirect standard output to a file
	    close(STDOUT_FILENO); 
	    open("./p4.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);

	// now exec "wc"...
        char *myargs[3];
        myargs[0] = strdup("wc");   // program: "wc" (word count)
        myargs[1] = strdup("p4.c"); // argument: file to count
        myargs[2] = NULL;           // marks end of array
        execvp(myargs[0], myargs);  // runs word count
    } else {
        // parent goes down this path (original process)
        int wc = wait(NULL);
  	    assert(wc >= 0);
    }
    return 0;
}
// Result

prompt> ./p4
prompt> cat p4.output
32 109 846 p4.c
prompt>

It would be quite boring if the child process could only execute the same program as the parent process.

So, we have exec() to give the child process more possibilities.

For example, the child process executed the program wc to count how many words are in the file p4.c.

Why do we need both fork() and exec() as different system calls? #

The biggest advantage of having fork() and exec() as two separate system calls is that: you have the opportunity to intervene before running the next program.

The most obvious example is the shell .

prompt> wc p3.c > newfile.txt

This example is the result of two processes cooperating.

  1. word count
  2. take word count result as input to create a new file.

Such a pattern can combine to create endless possibilities.

How to control a Process? #

Controlling a process is done by sending different signals to the process.

For example, we can use cmd + c (mac) or ctrl + c (windows) to stop a process; this is because this key combination sends a SIGINT signal to the process.

The process will then respond according to the different signals.

A computer may have many users using it at the same time, so operating systems have also developed the concept of user accounts to prevent users from issuing malicious commands to other people’s Processes.

Unix Utilities related to Processes #

  • ps: Displays the currently operating processes on the computer
  • top: Shows which processes are using the most resources