processes 2: signals & pipes cs 360 proc2. page 2 proc2 cs 360 wsu vancouver the big picture for...

29
Processes 2: Signals & Pipes CS 360 proc2

Upload: brooke-clark

Post on 17-Dec-2015

223 views

Category:

Documents


0 download

TRANSCRIPT

Processes 2: Signals & Pipes

CS 360

proc2

Page 2 proc2 CS 360 WSU Vancouver

The Big Picture

For processes to cooperate, they must share data why aren't files good enough?

Issue is called "inter-process communication" - IPC Unix has several IPC mechanisms

Basic IPC: pipes signals

Elegant IPC: Semaphores Shared Memory

Specialized IPC: named pipes record locking message queues FIFO's

future lecture

later in this lecture

Page 3 proc2 CS 360 WSU Vancouver

Agenda

Pipes

Signals

Atomic Operations

Lab Assignment

This week we continue learning about Unix processes - independently running programs

Page 4 proc2 CS 360 WSU Vancouver

Pipes

Page 5 proc2 CS 360 WSU Vancouver

The Invention of Pipes

The Unix inventors were trying to find the words in a file:

They added some syntax to the shell as an abbreviation:

They then had a flash of insight: the two programs could run simultaneously (fork/exec consequence) the temporary file doesn't have to exist on disk (could be just a kernel buffer) one program fills the buffer, the other empties it a tweak to open/close and read/write, and voila!

% tr " " "\n" < inputFile > tempFile% sort -u < tempFile > outputFile

% tr " " "\n" < inputFile | sort -u > outputFile

A Unix innovation: simple, useful

Page 6 proc2 CS 360 WSU Vancouver

What is a Pipe?

A pipe is a one-way communication channel between processes A simple but powerful technique for processes to communicate

process 1

process 2

write file descriptor

read file descriptor

kernel buffer

Logistics: a pipe is a kernel buffer with a "read" and and a "write" end kernel presents each end as a file descriptor (of course!) the processes uses normal read/write routines (again, nothing special) one process writes into one end; the other process reads from other end parent creates the file descriptors (using "pipe" routine) the descriptors are preserved across fork/exec (nothing special) writer waits if buffer is full, reader waits if buffer is empty

Page 7 proc2 CS 360 WSU Vancouver

A Closer Look at File Descriptors

parent processfile descriptor table

Descriptors may share an inode or pnode Fork duplicates the descriptor table Open/close/pipe etc. modify the table

child processfile descriptor tablefork

open

pipe

Page 8 proc2 CS 360 WSU Vancouver

Problem statement: parent will create a pipe and fork a child the parent writes to pipe, the child reads

A Pipe Example

#include <unistd.h>

int fd[2];pipe (fd);

if (fork ()) { while ( ... have more data ... ) {

write (fd[1], ... )}

} else { while (read (fd[0], ... )) {

... use the data ...}exit (0);

}

... parent continues ...

parent

child

fd[0]

fd[1]

the header

fd[0] = read end, fd[1] = write end

fork

write data into pipe for child to read

read data from pipe until eofcompute asynchronously from parent

Page 9 proc2 CS 360 WSU Vancouver

A Complete Pipe Example Problem statement:

parent will create a pipe and fork a child the parent will write to pipe, the child will read

#include <unistd.h>

int fd[2];pipe (fd);

if (fork ()) {close (fd[0]);while ( ... have more data ... ) {

write (fd[1], ... );}close (fd[1]);

} else {close (fd[1]);while (read (fd[0], ... )) {

... use the data ...}exit (0);

}

... parent continues ...

fd[0] = read end, fd[1] = write end

parent closes read endwrites data into pipe for child to read

child closes write endreads data from pipe until eofcomputes asynchronously from parent

Notes:• why close the end not used?

parent

child

close write end when done

fd[0]

fd[1]

the header

Page 10 proc2 CS 360 WSU Vancouver

Using Exec with Pipes

Consider this problem: calculate 1 * 2 * 3 * ... * 49 * 50

% bc < formula24

1 * 2 * 3 * 4formula

Approach: easy to code except the product is much too large for C int's or long's however, there is a neat program "bc" that handles any size integers:

% factorial 5030414093201713378043612608166064768844377641568960512000000000000

Using pipes, we can use "bc" from inside a program that we write:

Must fork/exec the bc program such that it's stdin = read end of a pipe Parent writes "1*2*3 *...*50\n" to the write end of the pipe Child reads that, computes, and writes its answer to stdout

Page 11 proc2 CS 360 WSU Vancouver

int main (int argc, char *argv[]) {

int i, n; /* i=1, 2, ..., n */int fd[2]; /* pipe file descriptors */int rdr, wtr; /* fd[0] and fd[1] */char text[10]; /* "*6" etc. */

assert (sscanf (argv[1], "%d", &n) == 1);assert (pipe (fd) >= 0);rdr = fd[0]; wtr = fd[1];

if (fork ()) { /* parent writes ... */close (rdr);write (wtr, "1", 1);for (i = 2; i <= n; ++i) {

sprintf (text, "*%d", i);write (wtr, text, strlen (text));

}write (wtr, "\n", 1);close (wtr);exit (0);

} else { /* child becomes bc and reads ... */close (wtr);close (0); dup (rdr); close (rdr);execlp ("bc", "bc", 0);fprintf (stderr, "%s: exec of bc failed\n", argv[0]);

exit (1);}

}

Factorial Program Source ...

parent

child

stdin

wtr

Page 12 proc2 CS 360 WSU Vancouver

Assignment

Write a program "connect" works like a shell pipeline

% connect friday : wc -l =

% friday | wc -l

The colon argument breaks argv into a left portion and a right portion

% connect friday : wc -l

left right parent

child

stdinstdout

Implementation will fork/exec and setup a pipe such that: parent: left portion runs with stdout = pipe write end child: right portion runs with stdin = pipe read endConsider swapping these parent/child functions… Why?

Page 13 proc2 CS 360 WSU Vancouver

Assignment (continued)

Due before class on February 25th (Wednesday)

Submit “connect.c” File connect.c should include all code and declarations other than library

components

Please note: The specification for this program is intentionally incomplete. Consider various situations and exception conditions that may occur. Determine a reasonable interpretation of the arguments, then design and implement a robust program.

Page 14 proc2 CS 360 WSU Vancouver

Dup: A Useful Variant of Open

"Dup" duplicates a file descriptor value new_fd = dup (old_fd);

some inodeor pipe end

process file descriptor table before ... ... and after dup (1)

some inodeor pipe end

Notes: a new descriptor is returned (in above case, returns 3) the new descriptor indexes same value as that the old descriptor indexes the new descriptor is smallest index not open (very useful!) There is another form: dup2(old_fd,new_fd) this form lets the

programmer specify the new file descriptor number rather than letting the kernel pick it. (If the new_fd is open, it is first closed)

Page 15 proc2 CS 360 WSU Vancouver

Some Pipe Details

Reads: if any data in pipe, return it (could be fewer bytes than requested) if pipe is empty and there is a writer, wait for data to be added to the pipe otherwise, return -1 (eof)

Writes: if there are no readers, return error (broken pipe) if data fits into pipe, add it atomically (as an indivisible action) otherwise, add data in chunks as readers make room and, if some readers are waiting, wake them up (so they can remove data)

These are simple and necessary rules

Page 16 proc2 CS 360 WSU Vancouver

An Exercise For You To Try

Situation: setup a parent and child process with the following relationship:

write file descriptor -- x[1] read file descriptor -- x[0]

parent child

read file descriptor -- y[0] write file descriptor -- y[1]

Page 17 proc2 CS 360 WSU Vancouver

Signals

Page 18 proc2 CS 360 WSU Vancouver

Interrupts

An "interrupt" is an event that can occur at an unpredictable time interrupt key received from keyboard

SIGINT alarm clock rings

SIGALRM child process terminates

SIGCHLD floating point exception

SIGFPE login connection hung up

SIGHUP urgent data received on network connection CPU time limit exceeded ...

Unix handles interrupts using a basic facility called "signals" each event has an associated integer code when the event occurs, the kernel "sends the signal" to the effected process the process then "receives the signal" and can take action

Writing reliable programs that respond to such events is difficult hard to anticipate all situations hard to create test conditions

Page 19 proc2 CS 360 WSU Vancouver

Signal Example 1: Critical Computation

Situation: a critical computation must not be interrupted by user’s ^C

Code skeleton:

#include <signal.h>critical_computation () {

signal (SIGINT, SIG_IGN);... do the computation ...signal (SIGINT, SIG_DFL);

}

begin ignoring SIGINT

restore default SIGINT action

signal code new signal action

Page 20 proc2 CS 360 WSU Vancouver

Signal Example 2: Mandatory Cleanup

Situation: a complicated computation must be always be properly cleaned up e.g.: temporary files must be deleted

Code skeleton:

#include <signal.h>complicated_computation () {

... prepare for computation ...signal (SIGINT, my_handler);... do the computation ...signal (SIGINT, SIG_DFL);cleanup ();

}

void my_handler() {printf ("\nInterrupt received!\n");cleanup ();exit (1):

}

set new handler for SIGINT

restore default SIGINT handler

receive SIGINT signal:cleanup computation and exit

Page 21 proc2 CS 360 WSU Vancouver

Set signal action:

previous setting is returned

Send signal to a process:

uid of killer must be superuser or equal to uid of target

Receive signal: if action is to ignore, do nothing (some signals don't permit this, e.g.: SIGKILL) if action is default, take default action (usually to terminate process) if action is to call handler:

– call the handler (argument is signal code after setting signal action back to default)

– upon return from handler, resume the process where it was interrupted

– if inside a blocked system call, return from that call with an error (e.g.: a read) Other routines:

signal (code, SIG_IGN); /* ignore signal */signal (code, SIG_DFL); /* take default action */signal (code, handler); /* call handler */

kill (pid, code); /* send signal to a process */

pause (); /* put process into wait state */alarm (seconds); /* send SIG_ALRM in seconds */

declared as: void handler (int);

Signal Routines Capsule Summary

Page 22 proc2 CS 360 WSU Vancouver

Signal Example 3: Sleep

Situation: write a routine "sleep" that makes process wait for specified seconds

Usage:

...sleep (5); /* process waits for (at least) 5 seconds */...

static void handler (int);

void sleep (int seconds) {signal (SIGALRM, handler);alarm (seconds);pause ();alarm (0);

}

static void handler (int code) {/* return makes the pause() return */

}

Code:

Page 23 proc2 CS 360 WSU Vancouver

Signal Example 4: Quick Reply Situation:

prompt user for a answer, but use default if user takes too long Code:static void handler (int code);static int expired;

void ask (char *question, char *answer, int seconds, char *default) {

int ok; /* fgets happy? */

fputs (question, stdout); fflush (stdout);

signal (SIGALRM, handler);expired = 0;alarm (seconds);fgets (answer, 100, stdin);alarm (0);

if (expired) strcpy (answer, default);

}

static void handler (int code) {expired = 1;/* return makes the fgets internal read return with error */

}

issue: how about EOF?

Why call alarm again?

Page 24 proc2 CS 360 WSU Vancouver

Example 5: Restart a Computation

Situation: if user hits interrupt key, stop computation and begin again e.g.: command line interpreter

Code:

#include <signal.h>#include <setjmp.h>static jmp_buf stack_state;static void handler (int);

void commmand_line_loop () {setjmp (stack_state);signal (SIGINT, handler);while (... get command ...) {

... interpret the command ...}signal (SIG_INT, SIG_DFL);

}

static void handler (int code) {printf ("\nCommand interrupted!\n");longjmp (stack_state, 0);

}

save stack state

restore stack statereturn 0 from setjmp

Advanc

ed

Page 25 proc2 CS 360 WSU Vancouver

Signal Example 2: An Improvement

#include <signal.h>static void my_handler (int);

complicated_computation () {int prev;

... prepare for computation ...

prev = signal (SIGINT, SIG_IGN);if (prev != SIG_IGN) signal (SIGINT, my_handler);

... do computation ...

if (prev != SIG_IGN) signal (SIGINT, prev);cleanup ();

}

static void my_handler (int code) {printf ("\nInterrupt received!\n");cleanup ();exit (1):

}

set new handler for SIGINTonly if not currently ignoring

receive SIGINT signal:cleanup computation and exit

restore old SIGINT handlerif we changed it

declare the handler interface

declare a variable "prev"

Advanc

ed

Page 26 proc2 CS 360 WSU Vancouver

Atomic Operations

Page 27 proc2 CS 360 WSU Vancouver

What If 2 Processes Manipulate 1 File?

What is a concrete example of when this might happen?

What are the possible outcomes?

think pessimistically!

What do you make of this?

Page 28 proc2 CS 360 WSU Vancouver

File Sharing & Atomic Operations Unix supports the sharing of open files between separate processes

provides the convenient stdin/stdout shell behavior without special cases gives programmers maximum control

The kernel operates like this: each process has its own file status flags ("open for reading?", ...) each process has its own "current offset" into the file bytes

The low level I/O operations are all "atomic" an operation either completes fully or fails fully

For example: bytes are read or written as a chunk without interruption open/close/lseek cannot be interrupted

This is a consistency promise, not a timing promise!

What are some consequences of this approach?

Page 29 proc2 CS 360 WSU Vancouver

Summary

The key process creation routines are: fork exec wait

The key process communication routines are: pipe dup, dup2

The key interrupt handling routines are signal setjmp (to restart a computation)