syscalls
Summary
POSIX syscalls for spawning processes
| Syscall | Include | Function |
|---|---|---|
fork() | <unistd.h> | duplicates the current process returns 0 in the child and child’s pid in the parent |
execl(path, arg...) | <unistd.h> | replace the current process image with a new one returns -1 only if error |
exit(status) | <unistd.h> | terminate the current process does not return, passes status to the parent process |
wait(status) | <unistd.h> | wait for one child process to terminate and stores it’s status returns the pid of the child process that terminates |
getpid() | <unistd.h> | returns the pid of the current process |
Concept
System calls
- API to the OS
- calling services in the kernel
- change to kernel mode
- UNIX:
- POSIX standards
- ~100 calls
- Windows:
- Win API
- ~1000 calls
Invocation
- function wraper
- library provides a version with the same name and parameters
- passes parameters 1-to-1 to the syscall
- function adapter
- library provides a more user friendly version
- handles more complicated setup for the syscall
c
char msg[] = "Hello world!";
// wrapper
write(1, msg, 13); // 1 is the fd for stdout
// adapter
printf(msg); // other parameters are handled by the function
use
straceon the binary to see the syscalls made by adapters
Mechanism
- user mode: program sets syscall register
- kernel mode: dispatcher checks the value and calls the corresponding handler
- user mode: return to program
Process hierachy
Master process
initprocesspid = 1- spawns all other processes via
fork()+exec()
Zombie processes
- process that has terminated
- hold onto process data until
wait()in the parent - parent terminates before child ->
initbecomes parent,initcallswait() - child terminates but parent doesn’t call
wait()-> child remains a zombie

Application
Forking
c
int result;
result = fork(); // A
if (result==0)
fork(); // B
else {
fork(); // C
fork(); // D
}
printf("Hello\n"); //how many? 6
return 0;
Waiting
- assuming that wait doesn’t block when there are no child processes
c
int main() {
// This is process P
if (fork() == 0) {
// This is process Q
if (fork() == 0) {
// This is process R
......
return 0;
}
<Point α>
}
<Point β>
return 0;
}
// breaking down into processes
// process P
if (fork() == 0) // parent, so skip
<Point β>
// process Q
if (fork() == 0) { // child, so enter
if (fork() == 0) // parent, so skip
<Point α>
}
<Point β>
// process R
if (fork() == 0) { // child, so enter
if (fork() == 0) { // child, so enter
......
return 0; // exit
| Point α | Point β | Behaviour |
|---|---|---|
wait(NULL) | P waits for Q Q waits for R | |
wait(NULL) | P doesn’t wait Q waits for R | |
wait(NULL) | wait(NULL) | P waits for Q Q waits for R |
execl(...) | wait(NULL) | P waits for Q Q may no longer wait |
wait(NULL) | execl(...) | P may no longer wait Q waits for R |
execl()replaces the process image, so any original code after it is replaced
Forking factorial
- scope is irrelevant, whole process is duplicated
c
int factorial(int n) {
if (n == 0) {
fork(); // forked up, only on the last iteration
return 1;
}
return factorial(n‐1) * n;
}
int main() {
printf("fac(2) = %d\n", factorial(2));
return 0;
}
// output
// fac(2) = 2
// fac(2) = 2