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) Parent process ID (ppid) Real User ID which this process Effective User ID unique integer ID of user/process started 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() Returns a new PID: e.g. pid == 5 Data Shared Program Child pid == 0 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 CHILD 3 CHILD 4 : PARENT 0 PARENT 1 PARENT 2 PARENT 3 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.
PID=981 Δημιουργία στο μοντέλο του UNIX: fork() Δεδομένα : p =? mypid =? Κείμενο: pid_t p, mypid; p = fork(); if (p < 0) { perror( fork ); 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 ); 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 ); 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 ); 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 ); 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 ); 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 ); else if (p == 0) { mypid = getpid(); child(); else { father();
Δημιουργία στο μοντέλο του UNIX: fork()
PID=981 Δημιουργία στο μοντέλο του UNIX: fork() Δεδομένα : p =? mypid =? Κείμενο: pid_t p, mypid; p = fork(); if (p < 0) { perror( fork ); 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 ); else if (p == 0) { mypid = getpid(); child(); else { father();
PID=981 PID=987 Δημιουργία στο μοντέλο του UNIX: fork() Δεδομένα : p =? mypid =? Κείμενο: pid_t p, mypid; p = fork(); if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father(); Δεδομένα : p =? mypid =? Κείμενο: pid_t p, mypid; p = fork(); if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father();
PID=981 PID=987 Δημιουργία στο μοντέλο του UNIX: fork() Δεδομένα : p = 987? mypid =? Κείμενο: pid_t p, mypid; p = fork(); p = 987 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father(); Δεδομένα : p = 0? mypid =? Κείμενο: pid_t p, mypid; p = fork(); p = 0 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father();
PID=981 PID=987 Δημιουργία στο μοντέλο του UNIX: fork() Δεδομένα : p = 987? mypid =? Κείμενο: pid_t p, mypid; p = fork(); p = 987 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father(); Δεδομένα : p = 0? mypid =? Κείμενο: pid_t p, mypid; p = fork(); p = 0 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father();
PID=981 PID=987 Δημιουργία στο μοντέλο του UNIX: fork() Δεδομένα : p = 987? mypid =? Κείμενο: pid_t p, mypid; p = fork(); p = 987 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father(); Δεδομένα : p = 0? mypid =? Κείμενο: pid_t p, mypid; p = fork(); p = 0 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father();
PID=981 PID=987 Δημιουργία στο μοντέλο του UNIX: fork() Δεδομένα : p = 987? mypid =? Κείμενο: pid_t p, mypid; p = fork(); p = 987 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father(); Δεδομένα : p = 0? mypid =? Κείμενο: pid_t p, mypid; p = fork(); p = 0 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father();
PID=981 PID=987 Δημιουργία στο μοντέλο του UNIX: fork() Δεδομένα : p = 987? mypid =? Κείμενο: pid_t p, mypid; p = fork(); p = 987 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father(); Δεδομένα : p = 0? mypid = 987? Κείμενο: pid_t p, mypid; p = fork(); p = 0 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father();
PID=981 PID=987 Δημιουργία στο μοντέλο του UNIX: fork() Δεδομένα : p = 987? mypid =? Κείμενο: pid_t p, mypid; p = fork(); p = 987 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father(); Δεδομένα : p = 0? mypid = 987? Κείμενο: pid_t p, mypid; p = fork(); p = 0 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father();
PID=981 PID=987 Δημιουργία στο μοντέλο του UNIX: fork() Δεδομένα : p = 987? mypid =? Κείμενο: pid_t p, mypid; p = fork(); p = 987 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father(); Δεδομένα : p = 0? mypid = 987? Κείμενο: pid_t p, mypid; p = fork(); p = 0 if (p < 0) { perror( fork ); else if (p == 0) { mypid = getpid(); child(); else { father();
Δημιουργία στο μοντέλο του UNIX: fork() Όλες οι διεργασίες προκύπτουν με fork() [σχεδόν όλες] Ίδιο πρόγραμμα με γονική διεργασία, αντίγραφο χώρου μνήμης, κληρονομεί ανοιχτά αρχεία, συνδέσεις, δικαιώματα πρόσβασης Αντικατάσταση προγράμματος διεργασίας: execve() Η γονική διεργασία ενημερώνεται για το θάνατο του παιδιού με wait() συλλογή τιμής τερματισμού (exit status) Μέχριτότε, παιδίπουέχεικαλέσειτηνexit() είναιzombie Αν ο γονέας πεθάνει πρώτα, η διεργασία γίνεται παιδί της init (PID = 1), που κάνει συνεχώς wait() prog-a 981 fork Prog-A 981 wait Prog-A 987 execve Prog-B 987 Prog-B exit 987 (zombie)
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); 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); 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"); if (p == 0) { child(); p = wait(&status); explain_wait_status(p, status); return 0;
Σωληνώσεις στο UNIX (1) Διεργασία write fd[1] M άκρο εγγραφής read fd[0] Χώρος Χρήστη Χώρος Πυρήνα άκρο ανάγνωσης Ένας από τους βασικότερους μηχανισμούς στο 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 fd[1] M άκρο εγγραφής int fd[2]; int num1, num2; read fd[0] Χώρος Χρήστη Χώρος Πυρήνα άκρο ανάγνωσης pipe(fd); write(fd[1], &num1, sizeof(num1)); read(fd[0], &num2, sizeof(num2));
Σωληνώσεις στο UNIX (1) Διεργασία write fd[1] read 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 fd[1] read fd[0] Χώρος Χρήστη Χώρος Πυρήνα άκρο εγγραφής άκρο ανάγνωσης pipe(fd); fork(); ο πατέρας κλείνει το άκρο ανάγνωσης τοπαιδίκλείνειτοάκροεγγραφής
Σωληνώσεις στο UNIX (2) Διεργασία πατέρας write fd[1] read fd[0] Χώρος Χρήστη Χώρος Πυρήνα άκρο εγγραφής άκρο ανάγνωσης pipe(fd); fork(); ο πατέρας κλείνει το άκρο ανάγνωσης τοπαιδίκλείνειτοάκροεγγραφής
Σωληνώσεις στο UNIX (2) Διεργασία πατέρας write fd[1] read fd[0] Διεργασία παιδί write fd[1] read fd[0] Χώρος Χρήστη Χώρος Πυρήνα άκρο εγγραφής άκρο ανάγνωσης pipe(fd); fork(); ο πατέρας κλείνει το άκρο ανάγνωσης τοπαιδίκλείνειτοάκροεγγραφής
Σωληνώσεις στο UNIX (2) Διεργασία πατέρας write fd[1] Διεργασία παιδί write fd[1] read fd[0] Χώρος Χρήστη Χώρος Πυρήνα άκρο εγγραφής άκρο ανάγνωσης pipe(fd); fork(); ο πατέρας κλείνει το άκρο ανάγνωσης τοπαιδίκλείνειτοάκροεγγραφής
Σωληνώσεις στο UNIX (2) Διεργασία πατέρας write fd[1] Διεργασία παιδί read fd[0] Χώρος Χρήστη Χώρος Πυρήνα άκρο εγγραφής άκρο ανάγνωσης pipe(fd); fork(); ο πατέρας κλείνει το άκρο ανάγνωσης τοπαιδίκλείνειτοάκροεγγραφής
Σωληνώσεις στο UNIX (2) Διεργασία πατέρας write fd[1] M άκρο εγγραφής Διεργασία παιδί read fd[0] Χώρος Χρήστη Χώρος Πυρήνα άκρο ανάγνωσης pipe(fd); fork(); ο πατέρας κλείνει το άκρο ανάγνωσης τοπαιδίκλείνειτοάκροεγγραφής
Σωληνώσεις στο UNIX (2) Διεργασία πατέρας write fd[1] Διεργασία παιδί read 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 ); p = fork(); if (p < 0) { perror( fork ); else if (p == 0) { if (read(pfd[0], &value, sizeof(value))!= sizeof(value)) { perror( read from pipe ); 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(0);
Κώδικας: παράδειγμα IPC με UNIX pipes double value; int pfd[2]; pid_t p; if (pipe(pfd) < 0) { perror( pipe ); p = fork(); if (p < 0) { perror( fork ); else if (p == 0) { if (read(pfd[0], &value, sizeof(value))!= sizeof(value)) { perror( read from pipe ); 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(0);