Вы находитесь на странице: 1из 19

Lecture 3

Chapter 3 of Robbins Book

Processes in UNIX

BIL 244 – System Programming


Process Identification

• Every time a program is executed it is given a unique integer


id. This is known as the process id or pid.
• Only the user who has created a process can modify this
process, this is know as ownership.
• The only exception to this rule is the root user who has the
ability to kill any process created by any user.
• To list processes the ps command is used as follows:
>ps
PID TTY TIME CMD
7073 pts/1 00:00:00 bash
7161 pts/1 00:00:00 xclock
7371 pts/1 00:00:00 ps
• The output of the ps command only shows the processes
running from the current terminal with the current user.

BIL 244 – System Programming


Process Identification (cont.)

• The getpid and getppid functions return the


process ID and the parents process ID,
respectively.

#include <uninstd.h>
pid_t getpid (void);
pid_t getppid(void)
•The pid_t is an unsigned integer type that represents a process ID

Neither of these functions can return an error

BIL 244 – System Programming


Process State

• The state of the process indicates its status at a particular time


The state diagram of a simple operating system is as follows

BIL 244 – System Programming


Killing Processes

• Sometimes it is desirable to stop a process from running on the system, to do


this two things are required.
• Firstly the pid of the process must be known and secondly the user trying to
kill the process must either own the process or be the root user.
• The kill command works by sending a signal to the currently running
process, depending upon the signal sent the process will respond in different
ways.
• A list of the common signals are shown on the next slide.
• On receipt of a signal one or more of the following actions are preformed:
Action Description
A Default action is to terminate the process
B Default action is to ignore the signal
C Default action is to terminate the process and dump core.
D Default action is to stop the process
E Signal cannot be caught
F Signal cannot be ignored.

BIL 244 – System Programming


Signals

BIL 244 – System Programming


Kill

• Usually the SIGTERM (15) signal is used to kill a process as thiswill try to shut
the process down properly:
# kill -15 <pid>
• Some processes do not respond to a SIGTERM and the SIGKILL (9) signal must
be used:
# kill -9 <pid>
• There are some processes which do not even respect the SIGKILL signal. For
such a process the last resort is to send the SIGHUP (1) signal to hang up the
connection:
# kill -1 <pid>
• To use kill in a program we use the following function:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig)
• Sends a signal to a process. The process to which the signal is to be sent is
specified by pid. The signal that is to be sent is specified by sig.
• Upon successful completion, 0 is returned. Otherwise, -1 is returned, no signal is
sent, and errno is set to indicate the error.

BIL 244 – System Programming


UNIX Process Creation and fork

• A process can create a new process by calling fork

#include <unistd.h>

pid_t fork(void)

Create a new process. The new (child) process is an exact copy of the
calling (parent) process. Upon successful completion, it will return 0 to
the child process and return the process ID of the child process to the
parent process. Otherwise, -1 is returned to the parent process, no child
process is created, and errno is set to indicate the error.

BIL 244 – System Programming


UNIX Process Creation and fork

• The new process created by fork is called the child process.


• This function is called once but returns twice.
• The only difference in the returns is that the return value in the child is
0 while the return value in the parent is the process ID of the new
child.
• The reason the child’s process ID is returned to the parent is because a
process can have more than one child, so there is no function that
allows a process to obtain the process ID’s of its children.
• The reason that fork returns 0 to the child is because a process can
have only have a single parent, so the child can always call getppid(2)
to obtain the process id of the parent.
• Both the child and parent continue executing with the instruction that
follows the call to fork. The child is a copy of the parent. For example
the child gets a copy of the parent’s data space, heap and stack. This
means that when a child is initially created it is an exact copy of the
parent.

BIL 244 – System Programming


The fork model

• It can be seen from the diagram that if the parent is killed


before the child calls an exit() to exit normally a
zombie process is created.
• This process is inherited by the init process which is part
of the Unix system boot. These process must then be
destroyed by the root user who owns the init function.

BIL 244 – System Programming


fork examples: #1

#include <stdio.h>

int main(void) {
pid_t c1, c2; /* process ids */

c2 = 0;
c1 = fork(); /* fork number 1 */
if (c1 == 0) c2 = fork(); /* fork number 2 */
fork(); /* fork number 3 */
if (c2 > 0) fork(); /* fork number 4 */

How many processes have been formed ?? By which process ??


And where ???
fork examples: #2

#include <stdio.h>
#include <unistd.h>
#include <errno.h>

int main(void) {

int pid; /* process id */

/* use fork to create a new process */


printf("calling fork()\n");
pid=fork();
/* check to see if fork worked */
if( pid <0 ) {
printf("Fork failed\n");
exit(0);
}
else if ( pid == 0 ) {
<<Child Process Code>>
}
else {
<<Parent Process Code>>
}
}
The wait function

• When a process crates a child, both parent and child


proceed with the execution from the point of fork. The
wait function causes the caller suspend execution until a
child’s status becomes available or until the caller receives
a signal
#include <sys/wait.h>

pid_t wait(int *stat_loc);


pid_t waitpid(pid_t pid, int *stat_loc, int options);

wait:
If a child terminated, return its pid therwise return -1 and set errno
waitpid:
Allows you to wait for a particular process, or all process if pid is -1.
Important option is NOHANG which will return 0 if there is a specified child
to wait for but it has not yet terminated.
BIL 244 – System Programming
The exec family of functions

• The exec family of functions: execl, execv, execle,


execve, execlp, and execvp replaces the current process
with a new one. If the function returns to the calling process, an
error has occurred; the return value is -1 and errno is set to
indicate the error.
• The main use for the fork function is to allow for the execution of
another process within a program, fork creates a child process
which is an exact clone of the parent process if we wish to execute
a new function after this has been done we use the exec family of
functions.
• These functions replace the current process (which after fork is a
clone of the parent) with the new process called by exec. This will
replace all of the text, data, stack and heap of the child process
with that of the new process called.
BIL 244 – System Programming
The exec family of functions
#include <unistd.h>

int execl( char *path, char *arg0, ..., char *argn, NULL)
int execv( char *path, char *argv[])
int execle(char *path, char *arg0, ..., char *argn, NULL,
char *envp[])
int execve(char *path, char *const argv[], char *envp[])
int execvp(char *file, char *const argv[])
int execlp(char *file, char *arg0, ..., char *argn, NULL)

l Command line arguments given as a NULL terminated list.


v Command line arguments given as an array.
e Environment variables given as an array.
p Search for file in the directories specified by the PATH environment variable.

• If any of the exec functions returns, an error will have occurred. The
return value is -1, and the global variable errno will be set to
indicate the error.

BIL 244 – System Programming


Fork-exec example:

#include <unistd.h> /* fork, execl, NULL */


#include <stdio.h> /* printf */
#include <sys/wait.h> /* waitpid */
#include <errno.h> /* EINTR */

int main(void) {
int c = 0;
pid_t pid;
int status = 0;

if ( ( pid = fork() ) < 0 ) {


status = -1; /* probably out of processes */
}
else if ( pid == 0 )
{
/* in child so we execute process */
execl("/bin/sh", "sh", "-c", "child2", (char *)NULL );
}
else {
<<Wait for Child>>
}
<<Parent continuation code>>
}
Fork-exec example: (cont.)

<<Wait for Child>>


/* now wait for the child to die */
while( waitpid( pid, &status, 0 ) < 0 )
{
printf("in wait pid\n");
if ( errno != EINTR )
{
status = -1;
break;
}
}

<<Parent continuation code>>


for ( c = 0; c < 100; c++ )
{
printf("parent here waiting");
}
printf("\nend of program");
return status;
Background Processes and Deamons

• Most shells interpret a line ending with & as a


command executed by a background process.

• When a shell creates a background process, it does


not wait for the process to complete before issuing a
prompt and accepting additional comments.

• A deamon is a background process that normally runs


indefinetly. Deamons handle mail, file transfer,
statistics, printer requests ...and so on.

BIL 244 – System Programming


Critical Sections
• When processes are simultaneously attempting to access a
shared resource, that is a resource which should be used by only
one process at a time, the portion of the code in which each
process accesses is called critical section.
• Programs with critical sections must be sure not to violate the
mutual exclution requirement
• One way of ensuring the mutual exclution requirement uses lock
mechanisms. Each process acquires a lock that excludes all other
processes before entering its critical section. When the process
finishes the critical section, it releases the lock.
• Unfortunately, this approach relies on the cooperation and
correctness of all participants. If one process fails to acquire the
lock before accessing the resource, the system fails.
• A common approach is to encapsulate shared resources in a
manner that ensures mutual exclusive access.

BIL 244 – System Programming