Linux Development Lecture 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 write to 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 interprocess 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.