0% found this document useful (0 votes)
70 views58 pages

UNIX System Programming: Processes

The document discusses processes in UNIX system programming. It begins by explaining that a process is the context maintained for an executing program, including code, data, registers, files, etc. It then discusses the fork() system call, which creates a child process by duplicating the calling process, and the wait() system call, which suspends a process until one of its child processes exits. The rest of the document provides more details on what comprises a process, examples of using fork() and wait(), and how process data is handled.

Uploaded by

Nazif Mal
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
70 views58 pages

UNIX System Programming: Processes

The document discusses processes in UNIX system programming. It begins by explaining that a process is the context maintained for an executing program, including code, data, registers, files, etc. It then discusses the fork() system call, which creates a child process by duplicating the calling process, and the wait() system call, which suspends a process until one of its child processes exits. The rest of the document provides more details on what comprises a process, examples of using fork() and wait(), and how process data is handled.

Uploaded by

Nazif Mal
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 58

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);
}

You might also like