- A single-processor kernel executes user processes by creating them, synchronizing them
by interleaving (only one processor is available) using scheduler/dispatcher, and waits for them to terminate.
- For creation, destroying and figuring out whether processes were completed, typically three primitives are used
by the OS kernel:
- fork(address) - creates a child process and makes it eligible for execution starting from address;
- quit() - terminates a process;
- join(name of child process) - it waits for a child process to terminate (to perform quit)
- In Unix, fork, quit and join are called fork, exit and wait, respectively.
- We can interpret n user processes as the implementation of co statement, where blue part of the code is executed by the OS monolithic
(i.e., atomic/sequential, and not concurrent) kernel (missing parts for interrupt service, and scheduler):
# co P1:S1; // ... // Pn:Sn; oc
# - where Pi are process names, and Si are statement list
for [i=1 to n] fork(Pi); # create the child processes
S1:P1;quit();..., Sn:Pn;quit();
# scheduler will decide in which order to execute them
for [i=1 to n] join(Pi); # wait for each child to quit
OS kernel waits for interrupt signals (events, SVC calls), handled by interrupt handler, executes fork, join, quit primitves, and it calls
the dispatcher which manages the list of ready processes to execute and the list of processes waiting for children to quit.
Each process is represented in the kernel by a process descriptor. When a process is idle, its decsriptor
contains its state or context - namely, all the information needed to execute the process. The state (context) includes the address
of the next instruction the process will executed and the contents of processor registers. The array of free, ready and waiting lists of
processes is a shared resource managed by OS.
User and kernel processes may interact using semaphores or monitors.