240 likes | 410 Views
CS 311 - Lecture 16 Outline. Inter-process Communication (IPC) Pipes Signals. Inter-Process Communication (IPC). IPC methods in UNIX Intermediate/temporary files (often together with I/O redirection) Pipes - IPC between two processes forked by a common ancestor process (unnamed)
E N D
CS 311 - Lecture 16 Outline • Inter-process Communication (IPC) • Pipes • Signals CS 311 - Operating Systems 1
Inter-Process Communication (IPC) • IPC methods in UNIX • Intermediate/temporary files (often together with I/O redirection) • Pipes - IPC between two processes forked by a common ancestor process (unnamed) • FIFOs (named pipes) - communication between any two processes on the same machine • Signals - communication between any two processes on the same machine • Sockets - communication between any two processes potentially separated by a network CS 311 - Operating Systems 1
Between processes IPC • I/O redirection with dup2() allows you to redirect input and output between processes and files • How to redirect input between processes on the same machine? • Answers: • Use temporary/intermediate files • Writes to disk are slow • No good way to track when the other process is ready to receive or send new data • Better answer: use pipes! CS 311 - Operating Systems 1
Pipes • Pipes provide a way to connect an output-only file descriptor in one process to an input-only file descriptor in another process Process 2 Process 1 read() fd_B write() fd_A CS 311 - Operating Systems 1
Creating a Pipe • Pipes are possible because file descriptors are shared across fork() and exec() • A parent process creates a pipe • Results in two new open file descriptors, one for input and one for output • The parent process forks (and possibly execs) • The child process now reads from the input file descriptor, and the parent process writes to the output file descriptor • or vice-versa CS 311 - Operating Systems 1
The pipe() function • Pipe() creates an unnamed pipe and returns two fds – one for read end (fd[0]) and other for write end (fd[1]). • Rules to processes on read end • If a process reads from a pipe whose write end has been closed, read() returns a 0 indicating end-of-input • If a process reads from an empty pipe whose write end is still open, it sleeps until some input is available. • If a process tries to read more bytes from a pipe than present, all of the contents are returned and read() returns the number of bytes read. CS 311 - Operating Systems 1
The pipe() function • Rules to processes that write to a pipe • If a process writes to a pipe whose read end has been closed, the write fails and the writer is sent a SIGPIPE signal. The default action is to terminate the writer. • If a process writes fewer bytes to a pipe than the pipe can hold, the writer process will complete its system call without being preempted by another process. If a process writes more, then no guarantees of atomicity. CS 311 - Operating Systems 1
Example code to create a pipe int r, p[2]; char message[512]; pid_t child1; if (pipe(p) == -1) { perror("pipe.c:pipe:"); exit(1); } child1 = fork(); switch (child1) { case 0: close(p[0]); /* (child) close the input file descriptor */ write(p[1], "hi parent process, this is the child!!", 41); exit(0); default: close(p[1]); /* (parent) close output file descriptor */ r = read(p[0], message, sizeof(message)); if (r > 0) { printf("Message received from child: %s\n", message); } exit(0); } CS 311 - Operating Systems 1
flow control -read() • read() • if data is available • read() gets the data and returns immediately • The return value of the read() function tells you how many bytes were read - it may be less than you requested • if data is not available, read() will block waiting for data (your process execution is suspended until data arrives) CS 311 - Operating Systems 1
flow control -write() • Write will not return until all the data has been written • Pipes have a certain size • Ex: only so much water will fit in a pipe between two points if data is not available, read() will block waiting for data (your process execution is suspended until data arrives) • If the pipe fills up, and there is no more room, write() will block until space becomes available (ie somebody reads the data from the pipe) CS 311 - Operating Systems 1
Pipes • Write() puts bytes in the pipe, read() takes them out • It is possible to determine the size of the pipe - see fpathconf() or the book read() fd_B write() fd_A CS 311 - Operating Systems 1
Programming note • Checking the return value of read() becomes very important. • Not just if return value is -1 • Also if return value is not equal to desired number of bytes read/written CS 311 - Operating Systems 1
Closing pipes • What happens if a process closes their end of the pipe when the other process is still trying to read or write to the pipe? • Process A closes output pipe. • If Process B is currently blocked on a read(), then process B's read() will return 0 • Process B closes input pipe • If process A tries to write to the pipe, write() will return -1, and errno will be set to EPIPE. Also process A will be sent the SIGPIPE signal. • Very important to catch the SIGPIPE signal! CS 311 - Operating Systems 1
Signals • When a user process wants to contact the kernel, it uses a system call • But how does the kernel initiate contact with a user process? • There are certain events that occur for which the kernel needs to notify a user process • The answer: • Signals! CS 311 - Operating Systems 1
Signals • Have many similarities to "exceptions" in Java and C++ • Although they are much less flexible than exceptions • There are a fixed set of signals • You cannot create your own signals CS 311 - Operating Systems 1
Signals are used for: • Kernel to process notifications: • Process has done something wrong (bus error, segmentation fault, etc) • A timer has expired (an alarm) • A child has completed executing • An event associated with the terminal has occurred (control-C, process trying to read from the background, etc) • The process on the other end of a communication link has gone away CS 311 - Operating Systems 1
Notification of Wrongdoing • Many signals result from the process doing something it is not supposed to, for example: • Trying to read or write to an invalid memory address (bus error - SIGBUS and segmentation fault - SIGSEGV) • Floating-point error - SIGFPE • Trying to execute an illegal machine instruction - SIGILL • Executing an illegal system call - SIGSYS • Why is a signal needed? • Gives the process a chance to: • clean up before being terminated • ignore the event if appropriate CS 311 - Operating Systems 1
Child has completed • Normally, the wait() and waitpid() calls will suspend until a child has completed executing • The signal allows a parent process to do other work instead of going to sleep and be notified when a child completes • Then when the SIGCHILD is received, the process can call wait() or waitpid() CS 311 - Operating Systems 1
Terminating Processes • Several signals (SIGINT, SIGTERM, SIGKILL) provide a way to indicate that other processes should terminate • Sent from user process to another user process • SIGINT and SIGTERM can be caught by the receiver, and potentially ignored • SIGKILL can not be caught or ignored - guaranteed to end a process CS 311 - Operating Systems 1
User-defined Signals • UNIX has reserved two signals for user-defined use • SIGUSR1 and SIGUSR2 • They have no special meaning to the kernel, but you can use them to signal between processes • Both the sending process and receiving process must agree on the meaning of SIGUSR1 and SIGUSR2 CS 311 - Operating Systems 1
Receiving Signals • A process can decide what and how it wants to handle signals that are received. • A process can define a different reaction to each kind of signal. • There are three options to handling each type of signal: • 1) Ignore it - continue executing as if it had never occurred • 2) Take the default action. This is different for each type of signal • 3) Specify a function that should be called when a type of signal arrives CS 311 - Operating Systems 1
sigaction() • Prototype: int sigaction(int signo, struct sigaction *act, struct sigaction *act) • First parameter is the signal type of interest • The second parameter is a pointer to a special sigaction structure • Describes the action to be taken upon receipt • The third parameter is a pointer to a another sigaction structure • The sigaction() function will use this pointer to write out what the setting were before the change was requested CS 311 - Operating Systems 1
The sigaction structure struct sigaction { void (*sa_handler)(int); //Handler Function sigset_t sa_mask; //Additional set of signals to be blocked during execution of signal-catching function. int sa_flags; //Special flags to affect behavior of signal. void (*sa_sigaction)(int, siginfo_t *, void *); //Pointer to a signal-catching function }; CS 311 - Operating Systems 1
(*sa_handler) - sigaction structure • The first attribute is set to one of three values: • SIG_DFL - take the default action • SIG_IGN - ignore the signal • A pointer to a function that should be called when this signal is received • Example: to ignore SIGINT signals main() { structsigaction act; act.sa_handler = SIG_IGN; sigaction(SIGINT, &act, NULL); while (1) { printf("sleeping…\n"); sleep(1); } exit(0); } CS 311 - Operating Systems 1