Linux Development Lecture 9 15.02.2016. Agenda Process ID Process spawning Process communication...

Preview:

DESCRIPTION

Process spawning

Citation preview

Linux DevelopmentLecture 9

15.02.2016

Agenda• Process ID• Process spawning• Process communication• Process signals• Deadlock• Deadlock handling• Mutex• Semaphore

Process spawning

Process ID• Every processhas a pid (ProcessID), a ppid (Parent

ProcessID), and a pgid (ProcessGroup ID). In addition every process has program code, data, variables, le descriptors, and an environment. • PID > 0 && PID <= 32768

Checking PIDs• #include <unistd.h> • pid_t getpid (void);

pid_t getppid (void) ;pid_t getpgid (void);

Process group• Besides having a pid, every process is also a member of

a process group. The Process Group ID (pgid) is also a positive integer, expressedin the pid_t type. Processescan join or leave process groups.

Process leader (Process parent)• A process group has a leader which has pid = pgid. The

group continues even if the group leader terminates, ending only when all of its members terminate.

Process group functions#include <unistd.h> int setpgid (pid_t pid, pid_t pgid);pid_t getpgid(pid_t pid);int setpgrp(void);pid_t getpgrp(void);

Process group functions• The function setpgid() sets the pgid of process pid to

pgid. If pid = 0,the current processpid is used. If pgid = O the pgid of process pid is used. • The function getpgid() returns the pgid of process pid. If

pid = O,the current process pid is used. • The function setpgrp() is equivalent to setpgid(0,0).• The function get-.pgrp() is equivalent to getpgid(0). • On success, these functions return 0. On failure, they

return -1 with errno set. The possible error returns are:

Process spawning

Using system()• int system (const char *cmd); // stdlib.h must be included• The system() call runs a process from inside another program: • In effect, system() invokes /bin/sh c cmd and sends the output

of cmd to standard output. • On success system() returns the exit code for cmd; if it fails

because it can t start a shell it returns 127 with any other error returning -1. • The calling program will block, waiting for system() to

complete its actions. One can use & to run cmd as a background process but results can be unpredictable.

Using fork()• Creating a new(child) process without terminating the

old one can be done with: #include <unistd.h>#include <sys/types.h>pid_t fork(void);

Using fork()• After a fork() two processes are running. The child

inherits the parent s resources. Only the pid, ppid, file locks and pending signals are not carried forth. File descriptors, memory, etc., are inherited. However, Linux uses a copy on-write implementation, so memory is not duplicated until it is actually altered.

Using vfork()• An additional process creation call, vfork() creates a

child that shares the memory address space of the parent. To prevent the parent from stepping on the child s use of the memory, the parent s execution is blocked until the child exits or issues an execve() call. Note that the child cannot writeto this address space and thus there is no need to copy page table entries.

Using exec()• #include <unisted.h>• int execl(const char *path, const char *arg, …...);• int execlp(const char *path, const char *arg, …...);• int execle(const char *path, const char *arg, …...);• int execv(const char *path, const char *argv[], …...);• int execvp(const char *path, const char *argv[], …...);• int execve(const char *path, const char *argv[], …...);

Using exec()• Replace the spawner process with the new one. The

new process has the same PID as the spawner. The new process is inheriting memory and open files from the old.• There should be no return value, if everything went fine.

If there is an error it will be returned.

Using clone()• In addition to the traditional fork() call that generates a

child that is a full peer to the parent,a so called heavyweight process, Linux permits the creation of so-called lightweight processes that share many kernel data structures (including memory areas) between parents and children; this is the basis of the multi-threaded programming implementation under Linux. • Used only for specific spawn purposes.

Exiting• There are several functions that are ending a process:• Void _exit(int status) // defined in unistd.h• Void exit(int status) // defined in stdlib.h• Void abort(void) //defined in stdlib.h

Normal termination• Calling return(int) – same as calling exit(int)• Calling exit(int) :• All open streams are flushed and closed• All handlers registred atexit() amd on_exit() are called• _exit(int) is called

• Calling _exit(int)• All files are closed, child processes are inherited by proces init

(PID:1) and SIGCHLD is sent to the parent process

Abnormal termination• An abort() generates SIGABRT and causes a core dump

to be generated. All open streams are flushed and closed. If SIGABRT is blocked or ignored, abort() will still terminate and core dump, but if the signal is caught it may be avoided.

Exit handlers• int atexit (void (*function)(void)); // defined in stdlib.h• int on_exit(void (*function)(int, void*), void *arg);

• Functions return 0 on success and -1 on error

Waiting• The parent process can wait (suspend execution) until

one of its child processes dies or is stopped, or a signal interrupts it.

• pid_t wait (int *status);• pid_t waitpid (pid_t pid, int * status, int options);

Waiting• pid value:• Pid < -1 : waiting for any child process with a process group id

= -pid• Pid = -1 : waiting for any child process (same as wait())• Pid = 0 : wait for any child process whose process group id is

the same as the calling process• Pid > 0 : wait for child process with ID = pid

Waiting• Return values:• EINVAL : option argument is invalid• ECHILD : pid doesn’t exist or it is not a child of the calling

process• ERESTARTSYS : SIGCHLD was caught.

Pipes and FIFOs

Pipes• Pipes permit processes to exchange data, not just short

signals. These processes may either be independent or have a parent child relationship. • A process may also use a pipe to receive input from or

direct output to a command.

popen() & pclose()• These functions are used to open and close a pipe using

the standard I/O library: • #include <stdio.h> • FILE *popen (const char *command, const char *mode); • int pclose (FILE *stream);

popen()• popen() initiates a processby creating a pipe, forking,

and invoking the shell, using the system() call. • The mode argument is the same as that used in fopen().

However it can take only the values r or w . Disallowed are r+ or w+ and append modes. • The command argument points to a null terminated

string which contains a shell command line which is passed to /bin/sh using the -c ag. (which reads commands from command and does any required interpretation. .)

pclose()• pclose() waits for the associated process to terminate

and returns the exit status of the command. On error it return -1 if an error is detected.

pipe()• The lower level pipe() function passes data between two

processes without the overhead of invoking a shell and gives more control: • A parent and child process may be connected or a

process may even connect to itself. • int pipe (int filedes[2]); • The pipe has a read end and a write end; Any data

written to filedes[1] can be read back from filedes[0]. Data is processed in a first-in, first-out (FIFO) basis.

FIFOs• Named pipes (or FIFO s) allow you to create a pipe

between two processes which may or may not have a common ancestor. FIFO stands for First In, First Out. • You can create a named pipe and wait for another

process to establish a communication link with it. • Unlike pipes, named pipes create an entry in the

lesystem that can then be accessed by another process.

FIFO creating with shell# cd /tmp# mkfifo afifo# cat < afifo & # ls -l > afifo

FIFO creating with system call• #include <sys/types.h> • #include <sys/stat.h> • int mkfifo (const char *pathname, mode_t mode ); • // for example int rc=mkfifo(“/tmp/afifo”, 0666);

FIFO• FIFO s may be opened for reading or writing but not for

both; i.e., in mode, O_RDWR is not allowed, but O_RDONLY and O_WRONLY may be used in combination with O_NONBLOK• The amount of information that can be in the pipe at

any one time is given by the size of a page of memory(which is 4096 bytes on X86) • On success 0 is returned, on error -1 is returned with

errno being set

FIFO creating with mknod• A FIFO can also be created with mknod() either as a

command line call, or a system library call: • int mknod (const char *pathname, mode_t mode, (dev_t

)0 ); • To create a FIFO entry in the filesystem, mode must be

comprised of the desired permissions, bitwise OR’ed with S_IFIFO. mnknod() is also used to create device special files.

Signals

Signals• Signals are software interrupts and provide a way to

handle asynchronous events. They provide a basic method of inter-process communication; the communicating processes may or may not be related and under most conditions the only information sent is the type of signal.

Signals• A process can send signals only to a process for which it

has the appropriate permissions .• Signals may be used to alert a process to take certain

actions or to die gracefully. For most signals, if a process receives it without first arranging to catch it the process will terminate. • If the process does not terminate execution will be

resumed by going back to the instruction that was being executed when the signal was received.• More info - $ man 7 signal

Signals available• $ kill –l• $ cat /usr/include/asm-generic/signal.h

Dispatching signals #include <sys/types.h> #include <signal.h>

int kill(pid_t pid, int sig); int raise(int sig);

kill()• kill() sends any signal to any process group or process.

Note that the sending process must have the proper permissions to send a signal to another process

raise()• raise() is used for a process to send signal to itself. • In a multithread process it sends a signal only to the

current thread

Alarm• #include <unistd.h>• Unsigned int alarm (usigned int seconds);• Sets alarm for certain amount of seconds. When the

seconds has expired a SIGALRM signal is raised.• Only one alarm is permitted per process. If another

alarm is set during countdown of one, the one is reset and started with the time of the another

Pausing and sleep• #include <unistd.h>

• int pause(void);• unsigned int sleep(unsigned int seconds);

pause()• pause() causes a process to sleep until a signal is

received. The function returns only if a signal handler is executed and that handler returns. In this case pause() returns -1 with errno set to EINTR.

sleep()• sleep() makes the current process sleep until seconds

have elapsed or a signal arrives which is not ignored. The function returns 0 if the requested time has elapsed, or the number of seconds left to sleep.

sleep()• #include <unistd.h>• void usleep(usec);

• #include <time.h>• struct timespec {time_t tv_sec; long tc_nsec;}• int nanosleep (const struct timespec *req, struct

timespec *rem);

sleep()• Usleep() – sleeps a process for microseconds• Nanosleep() – sleeps a process for nanoseconds

Signal handler• The signal handler is a handler that is responsible for

catching a signal and execute an action for handling the signal

Setting up a signal handler• #include <signal.h>

• void (*signal(int signum, void (*handler)(int)))(int);

• The signal() system call installs a new signal handler for the signal with number signum. The signal handlerissettohand1er() which may be a user specifed function,or one of the following: • SIG_IGN – ignore the signal• SIG_DFL – reset the signal to it’s default behaviour

Setting up a signal handler• The prototype is confusing. In practice looks like • typedef void (*sighandler_t)(int);• sighandler_t signal (int signum, sighandler_t handler);

Signal sets• It is possible to create a data type to represent multiple

signals, a so called signal set. With such a set one can tell the kernel to block, not block, or change the effect of any of the signals in the set on a given process.

Signal sets• Int sigemptyset (sigt_t *set);• Int sigfillset (sigset_t *set);• Int sigaddset (sigset_t *set, int signum);• Int sigdelset (sigset_t *set, int signum);• Int sigismember (const sigset_t *set, int signum);

Signals sets return• All functions but sigismember () return 0 on success, -1

on failiure. Sigismember () is returning 1 if the signal is member, 0 if not.

Signal sets• Int sigprocmask(int how, const sigset_t *set, sigset_t

*oldset);• Sigprocmask changes e list of currently blocked signals

according to the value of how:• SIG_BLOCK – signals from set are added to the signals of

oldset and all of them are blocked• SIG_UNBLOCK – signals specified in set are removed from

oldset

Deadlocks

Deadlock• A set of processes is deadlocked when each process in

the set is blocked awaiting an event that can only be triggered by another blocked process in the set• Typically involves processes competing for the same set of

resources• No efficient solution

Deadlock

Deadlock

Process P and Q (Deadlock is happening)

Process P and Q (Deadlock is not happening)

Deadlock conditions• Mutual exclusion• Only one process may use a resource at a time

• Hold-and-wait• A process may hold allocated resources while awaiting

assignment of others• No pre-emption• No resource can be forcibly removed form a process holding it

Dealing with deadlock• Three general approaches exist for dealing with

deadlock.• Prevent deadlock• Avoid deadlock• Detect Deadlock

Deadlock Prevention• Design a system in such a way that the possibility of

deadlock is excluded.• Two main methods• Indirect – prevent all three of the necessary conditions

occurring at once• Direct – prevent circular waits

Deadlock avoidance• A decision is made dynamically whether the current

resource allocation request will, if granted, potentially lead to a deadlock• Requires knowledge of future process requests

Deadlock avoidance• Process Initiation Denial• Do not start a process if its demands might lead to deadlock

• Resource Allocation Denial• Do not grant an incremental resource request to a process if

this allocation might lead to deadlock

Deadlock detection• Deadlock prevention strategies are very conservative; • limit access to resources and impose restrictions on processes.

• Deadlock detection strategies do the opposite• Resource requests are granted whenever possible.• Regularly check for deadlock

Mutex

Mutex• Suppose two or more processes require access to a

single nonsharable resource, such as a printer. • During the course of execution, each process will be

sending commands to the I/O device, receiving status information, sending data, and/or receiving data.• The code that needs the critical resource is called

critical section

Requirements for mutual exclusion• Only one process at a time is allowed in the critical

section for a resource• A process that halts in its noncritical section must do so

without interfering with other processes• No deadlock or starvation

Requirements for mutual exclusion• A process must not be delayed access to a critical

section when there is no other process using it• No assumptions are made about relative process speeds

or number of processes• A process remains inside its critical section for a finite

time only

Mutex POSIX• Int pthread_mutex_init(pthread_mutex_t *mutex, const

pthread_mutexattr_t *mutexattr);• Int pthread_mutex_lock(pthread_mutex_t *mutex);• Int pthread_mutex_unlock(pthread_mutex_t *mutex);• Int pthread_mutex_trylock(pthread_mutex_t *mutex);• Int pthread_mutex_destroy(pthread_mutex_t *mutex);

Semaphore

Semaphore• Multiple processes require single resource• In order to have no deadlock only one process must use

the resource.• The process to be using the resource is selected by a

integer counter

Semaphore

Semaphores POSIX• int sem_init (sem_t *sem, int pshared, unsigned int

value); • int sem_wait (sem_t *sem);• int sem_trywait (sem_t *sem);• int sem_post (sem_t *sem); • int sem_getvalue (sem_t *sem, int *sval); int

sem_destroy (sem_t *sem);

Starvation• This is the process when a process is needing a resource

which is not available.

Spinlock• This is the process when a process is waiting for a

mutex lock or semaphore

Daily task• 100 people must exit a room with one exit. Only one of

them can exit at a time and needs 200 ms to exit. Represent the peopl by spawning child processes and represent the door as a critical resource

Daily task 2• Do the same process but this time with 3 exits.

Recommended