OSTEP Chapter 5: Interlude - Process API Review
Table of Contents
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():
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.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.
- word count
- 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 computertop
: Shows which processes are using the most resources