UNIX System Programming
Processes
• Objectives
▫ look at how to program UNIX processes
▫ fork( ), wait( )
Overview
1. What is a Process?
2. fork()
3. wait()
4. Process Data
1. What is a Process?
A process is the context (the information/data)
maintained for an executing program.
Intuitively, a process is the abstraction of a physical
processor.
Exists because it is difficult for the OS to otherwise coordinate many concurrent
activities, such as incoming network data, multiple users, etc.
IMPORTANT: A process is sequential
What makes up a Process?
program code
machine registers
global data
stack
open files (file descriptors)
an environment (environment variables;
credentials for security)
Some of the Context Information
▫ Process ID (pid) unique integer
▫ Parent process ID (ppid)
▫ Real User ID ID of user/process
which started
this process
▫ Effective User ID ID of user who wrote
the process’ program
continued
Important System Processes
init – Mother of all processes. init is started at
boot time and is responsible for starting other
processes.
init uses file inittab & directories: /etc/rc?.d
getty – login process that manages login
sessions.
Unix Start Up Processes Diagram
OS kernel
Process 0
(sched)
Process 1
(init)
getty getty getty
login login
csh bash
Pid and Parentage
A process ID or pid is a positive integer that
uniquely identifies a running process, and is stored
in a variable of type pid_t.
You can get the process pid or parent’s pid
#include <sys/types>
main()
{
pid_t pid, ppid;
printf( "My PID is:%d\n\n",(pid = getpid()) );
printf( "Par PID is:%d\n\n",(ppid = getppid()) );
}
2. fork()
#include <sys/types.h>
#include <unistd.h>
pid_t fork( void );
Creates a child process by making a copy of the
parent process --- an exact duplicate.
Implicitly specifies code, registers, stack, data,
files
Both the child and the parent continue running.
fork() as a diagram
Parent
pid = fork() Child
Returns a new PID:
e.g. pid == 5 pid == 0
Shared
Program
Data
Data
Copied
Process IDs (pids revisited)
pid = fork();
In the child: pid == 0;
In the parent: pid == the process ID of the
child.
A program almost always uses this pid
difference to do different things in the parent
and child.
fork() Example (parchld.c)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid; /* could be int */
int i;
pid = fork();
if( pid > 0 )
{
/* parent */
for( i=0; i < 1000; i++ )
printf(“\t\t\tPARENT %d\n”, i);
}
else
{
/* child */
for( i=0; I < 1000; i++ )
printf( “CHILD %d\n”, i );
}
return 0;
}
Possible Output
CHILD 0
CHILD 1
CHILD 2
PARENT 0
PARENT 1
PARENT 2
PARENT 3
CHILD 3
CHILD 4
PARENT 4
:
Things to Note
i is copied between parent and child.
The switching between the parent and child
depends on many factors:
machine load, system process scheduling
I/O buffering effects amount of output shown.
Output interleaving is nondeterministic
cannot determine output by looking at code
3. wait()
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *statloc);
Suspends the calling process until one of its child processes
ends. If the system already has information about a
terminated child process when wait is called, the return
from wait occurs immediately.
Status information for the terminating child process is usually
stored at the location pointed to by *statloc. if wait is called
with NULL as the *statloc value, no status information is
returned.
If successful, wait returns the process ID of the child process.
If unsuccessful, a -1 is returned.
wait() Actions
A process that calls wait() can:
suspend (block) if all of its children are still
running, or
return immediately with the termination status of
a child, or
return immediately with an error if there are no
child processes.
4. Process Data
Since a child process is a copy of the parent, it
has copies of the parent’s data.
A change to a variable in the child will not
change that variable in the parent.
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα :
p=? mypid = ?
Κείμενο:
pid_t p, mypid;
…
p = fork();
if (p < 0) {
perror(“fork”);
exit(1);
} else if (p == 0) {
mypid = getpid();
child();
} else {
father();
}
PID=981
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα :
p=? mypid = ?
Κείμενο:
pid_t p, mypid;
…
p = fork();
if (p < 0) {
perror(“fork”);
exit(1);
} else if (p == 0) {
mypid = getpid();
child();
} else {
father();
}
PID=981
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα :
p=? mypid = ?
Κείμενο:
pid_t p, mypid;
…
p = fork();
if (p < 0) {
perror(“fork”);
exit(1);
} else if (p == 0) {
mypid = getpid();
child();
} else {
father();
}
PID=981
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα :
p = -1
? mypid = ?
Κείμενο:
pid_t p, mypid;
…
p = fork(); p = -1, errno =
if (p < 0) { ENOMEM
perror(“fork”);
exit(1);
} else if (p == 0) {
mypid = getpid();
child();
} else {
father();
}
PID=981
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα :
p = -1
? mypid = ?
Κείμενο:
pid_t p, mypid;
…
p = fork(); p = -1, errno =
if (p < 0) { ENOMEM
perror(“fork”);
exit(1);
} else if (p == 0) {
mypid = getpid();
child();
} else {
father();
}
PID=981
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα :
p = -1
? mypid = ?
Κείμενο:
pid_t p, mypid;
…
p = fork(); p = -1, errno =
if (p < 0) { ENOMEM
perror(“fork”);
exit(1);
} else if (p == 0) {
mypid = getpid();
child();
} else {
father();
}
PID=981
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα :
p = -1
? mypid = ?
Κείμενο:
pid_t p, mypid;
…
p = fork(); p = -1, errno =
if (p < 0) { ENOMEM
perror(“fork”);
exit(1);
} else if (p == 0) {
mypid = getpid();
child();
} else {
father();
}
PID=981
Δημιουργία στο μοντέλο του UNIX: fork()
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα :
p=? mypid = ?
Κείμενο:
pid_t p, mypid;
…
p = fork();
if (p < 0) {
perror(“fork”);
exit(1);
} else if (p == 0) {
mypid = getpid();
child();
} else {
father();
}
PID=981
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα :
p=? mypid = ?
Κείμενο:
pid_t p, mypid;
…
p = fork();
if (p < 0) {
perror(“fork”);
exit(1);
} else if (p == 0) {
mypid = getpid();
child();
} else {
father();
}
PID=981
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα : Δεδομένα :
p=? mypid = ? p=? mypid = ?
Κείμενο: Κείμενο:
pid_t p, mypid; pid_t p, mypid;
… …
p = fork(); p = fork();
if (p < 0) { if (p < 0) {
perror(“fork”); perror(“fork”);
exit(1); exit(1);
} else if (p == 0) { } else if (p == 0) {
mypid = getpid(); mypid = getpid();
child(); child();
} else { } else {
father(); father();
} }
PID=981 PID=987
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα : Δεδομένα :
p =987
? mypid = ? p =0
? mypid = ?
Κείμενο: Κείμενο:
pid_t p, mypid; pid_t p, mypid;
… …
p = fork(); p = 987 p = fork(); p = 0
if (p < 0) { if (p < 0) {
perror(“fork”); perror(“fork”);
exit(1); exit(1);
} else if (p == 0) { } else if (p == 0) {
mypid = getpid(); mypid = getpid();
child(); child();
} else { } else {
father(); father();
} }
PID=981 PID=987
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα : Δεδομένα :
p =987
? mypid = ? p =0
? mypid = ?
Κείμενο: Κείμενο:
pid_t p, mypid; pid_t p, mypid;
… …
p = fork(); p = 987 p = fork(); p = 0
if (p < 0) { if (p < 0) {
perror(“fork”); perror(“fork”);
exit(1); exit(1);
} else if (p == 0) { } else if (p == 0) {
mypid = getpid(); mypid = getpid();
child(); child();
} else { } else {
father(); father();
} }
PID=981 PID=987
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα : Δεδομένα :
p =987
? mypid = ? p =0
? mypid = ?
Κείμενο: Κείμενο:
pid_t p, mypid; pid_t p, mypid;
… …
p = fork(); p = 987 p = fork(); p = 0
if (p < 0) { if (p < 0) {
perror(“fork”); perror(“fork”);
exit(1); exit(1);
} else if (p == 0) { } else if (p == 0) {
mypid = getpid(); mypid = getpid();
child(); child();
} else { } else {
father(); father();
} }
PID=981 PID=987
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα : Δεδομένα :
p =987
? mypid = ? p =0
? mypid = ?
Κείμενο: Κείμενο:
pid_t p, mypid; pid_t p, mypid;
… …
p = fork(); p = 987 p = fork(); p = 0
if (p < 0) { if (p < 0) {
perror(“fork”); perror(“fork”);
exit(1); exit(1);
} else if (p == 0) { } else if (p == 0) {
mypid = getpid(); mypid = getpid();
child(); child();
} else { } else {
father(); father();
} }
PID=981 PID=987
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα : Δεδομένα :
p =987
? mypid = ? p =0
? mypid = 987
?
Κείμενο: Κείμενο:
pid_t p, mypid; pid_t p, mypid;
… …
p = fork(); p = 987 p = fork(); p = 0
if (p < 0) { if (p < 0) {
perror(“fork”); perror(“fork”);
exit(1); exit(1);
} else if (p == 0) { } else if (p == 0) {
mypid = getpid(); mypid = getpid();
child(); child();
} else { } else {
father(); father();
} }
PID=981 PID=987
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα : Δεδομένα :
p =987
? mypid = ? p =0
? mypid = 987
?
Κείμενο: Κείμενο:
pid_t p, mypid; pid_t p, mypid;
… …
p = fork(); p = 987 p = fork(); p = 0
if (p < 0) { if (p < 0) {
perror(“fork”); perror(“fork”);
exit(1); exit(1);
} else if (p == 0) { } else if (p == 0) {
mypid = getpid(); mypid = getpid();
child(); child();
} else { } else {
father(); father();
} }
PID=981 PID=987
Δημιουργία στο μοντέλο του UNIX: fork()
Δεδομένα : Δεδομένα :
p =987
? mypid = ? p =0
? mypid = 987
?
Κείμενο: Κείμενο:
pid_t p, mypid; pid_t p, mypid;
… …
p = fork(); p = 987 p = fork(); p = 0
if (p < 0) { if (p < 0) {
perror(“fork”); perror(“fork”);
exit(1); exit(1);
} else if (p == 0) { } else if (p == 0) {
mypid = getpid(); mypid = getpid();
child(); child();
} else { } else {
father(); father();
} }
PID=981 PID=987
Δημιουργία στο μοντέλο του UNIX: fork()
Όλες οι διεργασίες προκύπτουν με fork() [σχεδόν όλες]
Ίδιο πρόγραμμα με γονική διεργασία, αντίγραφο χώρου μνήμης,
κληρονομεί ανοιχτά αρχεία, συνδέσεις, δικαιώματα πρόσβασης
Αντικατάσταση προγράμματος διεργασίας: execve()
Η γονική διεργασία ενημερώνεται για το θάνατο του
παιδιού με wait() → συλλογή τιμής τερματισμού (exit status)
Μέχρι τότε, παιδί που έχει καλέσει την exit() είναι zombie
Αν ο γονέας πεθάνει πρώτα, η διεργασία γίνεται παιδί της
init (PID = 1), που κάνει συνεχώς wait()
prog-A Prog-A
fork wait
981 981
Prog-A 987 Prog-B 987 (zombie)
Prog-B
execve 987 exit
wait() / waitpid()
Για κάθε fork() πρέπει να γίνει ένα wait()
wait(&status)
Μπλοκάρει έως οποιοδήποτε παιδί πεθάνει
Το status κωδικοποιεί πώς πέθανε η διεργασία
Κανονικά (exit()), λόγω κάποιου σήματος (SIGTERM, SIGKILL)
Χρήσιμες μακροεντολές για την ερμηνεία του status
WIFEXITED(), WEXITSTATUS(), WIFSIGNALED(), WTERMSIG()
σας δίνεται η explain_wait_status()
Μια πιο ευέλικτη wait(): waitpid()
Περιμένει για αλλαγή κατάστασης συγκεκριμένου ή
οποιουδήποτε PID διεργασίας-παιδιού
Συμπεριφορά ελεγχόμενη από flags (WNOHANG, WUNTRACED)
explain_wait_status()
void explain_wait_status(pid_t pid, int status)
{
if (WIFEXITED(status))
fprintf(stderr, "Child with PID = %ld terminated normally, exit status = %d\n",
(long)pid, WEXITSTATUS(status));
else if (WIFSIGNALED(status))
fprintf(stderr, "Child with PID = %ld was terminated by a signal, signo = %d\n",
(long)pid, WTERMSIG(status));
else if (WIFSTOPPED(status))
fprintf(stderr, "Child with PID = %ld has been stopped by a signal, signo = %d\n",
(long)pid, WSTOPSIG(status));
else {
fprintf(stderr, "%s: Internal error: Unhandled case, PID = %ld, status = %d\n",
__func__, (long)pid, status);
exit(1);
}
fflush(stderr);
}
explain_wait_status()
void explain_wait_status(pid_t pid, int status)
{
if (WIFEXITED(status))
fprintf(stderr, "Child with PID = %ld terminated normally, exit status = %d\n",
(long)pid, WEXITSTATUS(status));
else if (WIFSIGNALED(status))
fprintf(stderr, "Child with PID = %ld was terminated by a signal, signo = %d\n",
(long)pid, WTERMSIG(status));
else if (WIFSTOPPED(status))
fprintf(stderr, "Child with PID = %ld has been stopped by a signal, signo = %d\n",
(long)pid, WSTOPSIG(status));
else {
fprintf(stderr, "%s: Internal error: Unhandled case, PID = %ld, status = %d\n",
__func__, (long)pid, status);
exit(1);
}
fflush(stderr);
}
Παράδειγμα:
pid = wait(&status);
explain_wait_status(pid, status);
if (WIFEXITED(status) || WIFSIGNALED(status))
--processes_alive;
Κώδικας: παράδειγμα fork() / wait()
void child(void)
{
compute(10000);
exit(7);
}
int main(void)
{
pid_t p;
int status;
p = fork();
if (p < 0) {
perror("fork");
exit(1);
}
if (p == 0) {
child(); exit(1);
}
p = wait(&status);
explain_wait_status(p, status);
return 0;
}
Σωληνώσεις στο UNIX (1)
Διεργασία
write read Χώρος Χρήστη
fd[1] fd[0]
Χώρος Πυρήνα
M
άκρο άκρο
εγγραφής ανάγνωσης
Ένας από τους βασικότερους μηχανισμούς στο UNIX
Μονόδρομη μεταφορά δεδομένων
Από το άκρο εγγραφής στο άκρο ανάγνωσης
Δημιουργία με pipe(), επικοινωνία με write() και read()
Αν η σωλήνωση είναι άδεια; → η read() μπλοκάρει
Σωληνώσεις στο UNIX (1)
Διεργασία
Χώρος Χρήστη
Χώρος Πυρήνα
int fd[2];
int num1, num2;
pipe(fd);
write(fd[1], &num1, sizeof(num1));
read(fd[0], &num2, sizeof(num2));
Σωληνώσεις στο UNIX (1)
Διεργασία
Χώρος Χρήστη
Χώρος Πυρήνα
άκρο άκρο
εγγραφής ανάγνωσης
int fd[2];
int num1, num2;
pipe(fd);
write(fd[1], &num1, sizeof(num1));
read(fd[0], &num2, sizeof(num2));
Σωληνώσεις στο UNIX (1)
Διεργασία
write Χώρος Χρήστη
fd[1]
Χώρος Πυρήνα
M
άκρο άκρο
εγγραφής ανάγνωσης
int fd[2];
int num1, num2;
pipe(fd);
write(fd[1], &num1, sizeof(num1));
read(fd[0], &num2, sizeof(num2));
Σωληνώσεις στο UNIX (1)
Διεργασία
write read Χώρος Χρήστη
fd[1] fd[0]
Χώρος Πυρήνα
M
άκρο άκρο
εγγραφής ανάγνωσης
int fd[2];
int num1, num2;
pipe(fd);
write(fd[1], &num1, sizeof(num1));
read(fd[0], &num2, sizeof(num2));
Σωληνώσεις στο UNIX (1)
Διεργασία
write read Χώρος Χρήστη
fd[1] fd[0]
Χώρος Πυρήνα
άκρο άκρο
εγγραφής ανάγνωσης
int fd[2];
int num1, num2;
pipe(fd);
write(fd[1], &num1, sizeof(num1));
read(fd[0], &num2, sizeof(num2));
Σωληνώσεις στο UNIX (2)
Διεργασία
πατέρας
Χώρος Χρήστη
Χώρος Πυρήνα
pipe(fd);
fork();
… ο πατέρας κλείνει το άκρο ανάγνωσης
…το παιδί κλείνει το άκρο εγγραφής
Σωληνώσεις στο UNIX (2)
Διεργασία
πατέρας
write read Χώρος Χρήστη
fd[1] fd[0]
Χώρος Πυρήνα
άκρο άκρο
εγγραφής ανάγνωσης
pipe(fd);
fork();
… ο πατέρας κλείνει το άκρο ανάγνωσης
…το παιδί κλείνει το άκρο εγγραφής
Σωληνώσεις στο UNIX (2)
Διεργασία
πατέρας
write read Χώρος Χρήστη
fd[1] fd[0]
Χώρος Πυρήνα
άκρο άκρο
εγγραφής ανάγνωσης
pipe(fd);
fork();
… ο πατέρας κλείνει το άκρο ανάγνωσης
…το παιδί κλείνει το άκρο εγγραφής
Σωληνώσεις στο UNIX (2)
Διεργασία Διεργασία
πατέρας παιδί
write read write read Χώρος Χρήστη
fd[1] fd[0] fd[1] fd[0]
Χώρος Πυρήνα
άκρο άκρο
εγγραφής ανάγνωσης
pipe(fd);
fork();
… ο πατέρας κλείνει το άκρο ανάγνωσης
…το παιδί κλείνει το άκρο εγγραφής
Σωληνώσεις στο UNIX (2)
Διεργασία Διεργασία
πατέρας παιδί
write write read Χώρος Χρήστη
fd[1] fd[1] fd[0]
Χώρος Πυρήνα
άκρο άκρο
εγγραφής ανάγνωσης
pipe(fd);
fork();
… ο πατέρας κλείνει το άκρο ανάγνωσης
…το παιδί κλείνει το άκρο εγγραφής
Σωληνώσεις στο UNIX (2)
Διεργασία Διεργασία
πατέρας παιδί
write read Χώρος Χρήστη
fd[1] fd[0]
Χώρος Πυρήνα
άκρο άκρο
εγγραφής ανάγνωσης
pipe(fd);
fork();
… ο πατέρας κλείνει το άκρο ανάγνωσης
…το παιδί κλείνει το άκρο εγγραφής
Σωληνώσεις στο UNIX (2)
Διεργασία Διεργασία
πατέρας παιδί
write read Χώρος Χρήστη
fd[1] fd[0]
Χώρος Πυρήνα
M
άκρο άκρο
εγγραφής ανάγνωσης
pipe(fd);
fork();
… ο πατέρας κλείνει το άκρο ανάγνωσης
…το παιδί κλείνει το άκρο εγγραφής
Σωληνώσεις στο UNIX (2)
Διεργασία Διεργασία
πατέρας παιδί
write read Χώρος Χρήστη
fd[1] fd[0]
Χώρος Πυρήνα
άκρο άκρο
εγγραφής ανάγνωσης
pipe(fd);
fork();
… ο πατέρας κλείνει το άκρο ανάγνωσης
…το παιδί κλείνει το άκρο εγγραφής
Κώδικας: παράδειγμα IPC με UNIX pipes
Κώδικας: παράδειγμα IPC με UNIX pipes
double value;
int pfd[2];
pid_t p;
if (pipe(pfd) < 0) {
perror(“pipe”);
exit(1);
}
p = fork();
if (p < 0) {
perror(“fork”);
exit(1);
} else if (p == 0) {
if (read(pfd[0], &value, sizeof(value)) != sizeof(value)) {
perror(“read from pipe”);
exit(1);
}
printf(“child received value: value = %f\n”, value);
exit(0);
} else {
compute_value(&value);
if (write(pfd[1], &value, sizeof(value)) != sizeof(value)) {
perror(“write to pipe”);
exit(1);
}
exit(0);
}
Κώδικας: παράδειγμα IPC με UNIX pipes
double value;
int pfd[2];
pid_t p;
if (pipe(pfd) < 0) {
perror(“pipe”);
exit(1);
}
p = fork();
if (p < 0) {
perror(“fork”);
exit(1);
} else if (p == 0) {
if (read(pfd[0], &value, sizeof(value)) != sizeof(value)) {
perror(“read from pipe”);
exit(1);
}
printf(“child received value: value = %f\n”, value);
exit(0);
} else {
compute_value(&value);
if (write(pfd[1], &value, sizeof(value)) != sizeof(value)) {
perror(“write to pipe”);
exit(1);
}
exit(0);
}