Updated:

4 minute read

1. Recall

As we mentioned before, a process is an instance of a program executing. Its state and information are all monitored by the OS. Processes can do their work by invoking system calls.

But is there any operations that are on their own?

Yes, process can create a new process (sometimes called sub-process) by copying itself!


2. fork()

  • In typical UNIX systems (exclude Linux in a sense that it may somehow augment the child process in the first place), the fork() system call (or library precisely) creates a copy of the callee process.

  • When I say copy, I mean all of the states of the original process duplicated in both the parent and the child! (Memory, File Descriptors, etc…)

191689222fa2f0ce00ab8e0d856f0e19.png

  • fork() returns 3 kinds of values:
    • ` 0 ` represents that this process is the parent;
    • ` 1 ` represents that this is process is the child;
    • -1 (as UNIX convention) represents an error message.
  • Why we need those return values?
    • when we call fork(), the original process will be trapped into the kernel mode and halt until it returns.
    • when it returns, the kernel will actually return twice, once in the parent process and once in the child.
    • the parent and the child starts from the instruction after the fork() together.

An example:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#define BUFSIZE 1024
int main(int argc, char *argv[])
{
    char buf[BUFSIZE];
    size_t readlen, writelen, slen;
    pid_t cpid, mypid;
    pid_t pid = getpid(); /* get current processes PID */
    printf("Parent pid: %d\n", pid);
    cpid = fork();
    if (cpid > 0) { 
    /* Parent Process */
        mypid = getpid();
        printf("[%d] parent of [%d]\n", mypid, cpid);
    } 
    else if (cpid == 0) { 
    /* Child Process */
        mypid = getpid();
        printf("[%d] child\n", mypid);
    }
    else {
        perror("Fork failed");
        exit(1);
    }
    exit(0);
}

3. The relationship between parent and child processes

  • fork() is executed in an unblocked manner, which means the parent process will not naturally sit there and wait for their child processes to return.

  • Furthermore, they are actually running in parallel that both of them are exchanging time on the scheduler queue and the run queue.

  • The parent processes are able to control their children processes directly.

  • What if the child gets “killed” right away before its parent does anything?
    • Basically, when this happens, the PCB of the child will not be reallocated by the OS;
    • Instead, these dead children will stay in a state called the Zombie State. (When you mess up the so-called interprocess communication between parent and children process, there will be a bunch of “Zombie” appear)
  • What if the parent gets “killed” before the child process return?
    • Generally, the child will get inherited by a grandparent.

// Didn’t expect it’s going to be such a creepy parenting blog …


4. Shell

A shell is a job control system which allows programmers to create and manage a set of programs to do some task —— Berkeley CS162

A shell is a command interpreter which makes key process-management system calls that are dealing with the creation and termination of processes. —— Prof. Andy Tanenbaum

Okay~ how come we wind up with Shell anyway?

  • Every process has a parent and the parent also has its parent and so forth. We can go on and on and on until we hit something called init in UNIX;

ecddae82fa5a8a474579c7beaa017810.png 4a71fe295fce88f7c0226b9185f40222.png

  • The init is the first process which calls all other children processes and one of which is the shell.

64e229116c5d679cd8d9b8380099d613.png

  • The shell, whose job is to create and manipulate processes us, turns out to be a special process that fork()s itself and immediately calls exec() to load a new program into its memory address. (This often followed with wait() that blocks itself until gets the return value from the child, which releases the process from being a zombie.)

1b3f75dcc541df051af709f790eb89c6.png

cc –c sourcefile1.c
cc –c sourcefile2.c
ln –o program sourcefile1.o sourcefile2.o
./program
  • The way that the Shell works is also how we start up a parallel program.

d0dcb4ad06069ef8cb758ff3033c1192.png


5. UNIX Process Management

d93f88765dc551fa3e4d24a102944beb.png

  • UNIX fork – system call to create a copy of the current process, and start it running.
    • No arguments!
  • UNIX exec – system call to change the program being run by the current process (replace the current running process with a brand new process).

  • UNIX wait – system call to wait for a process to finish.

  • UNIX signal – system call to send a notification to another process.

( To me, the signal service is just a sort of user-level interruption that works in any process regardless it’s a parent or child. )

An example of using the signal():

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

// this is just a way of changing the original `SIGINT` handler to our self-defined hander

void signal_callback_handler(int signum) {
  printf("Caught signal %d - phew!\n", signum);
  exit(1);
}

int main() {
  signal(SIGINT, signal_callback_handler);
  while (1) { }
}

Leave a comment