CS 111 Lecture 6 Scribe Notes - Winter 2012
by Yuting Wang, Armando De los Santos and Greg
Rivera for a lecture by Professor Paul Eggert on January 30, 2012
Table of Contents
- Pipe
- What's a pipe
- Advantages
- Disavantages
- An example
- Problems with pipes
- Signal handling
- Motivation
- The API for signal handling
- Some other issues
What's a pipe
- process A | process B. The output of process A is redirected as the input of process B
- Sockets is like a pipe on different hosts.
Advantages
- Since pipe is implemented entirely in RAM, it doesn't need any temp file or disk I/O.
- A pipe has a bounded buffer in it, so save RAM.
Disadvantages
- Pipe has to be read and written sequentially, thus no random access available.
- We can use shared memory instead, it allows randon access, it's faster, but it has synchronization issues.
An example
du | sort -n: sort files according to their space usage.
- In the root process, call int pipe(fd[2]). It returns 1 if succeeds, otherwise 0. The file descriptor of the reading end is generated and saved in fd[0], writing end in fd[1].
- fork() to get a child process.
-
The child should close the write end of the pipe and dup2 to stdin, then execute sort.
- The parent should call fork() again to let the second child close the read end of the pipe by calling close() then execute du. While the parent itself should wait for the sort process to finish, and close both end of the pipe.
Problems with pipes
-
What if writer is done? The read will get 0 byte from the pipe, which is EOF.
-
What if the reader is done? write() yields -1 errno.
-
Another option you can have with pipes:
while (pipe(fd) == 0) continue; //There's no garbage collectors for pipe in a running process.
Signal Handling: Motivation
-
Let's start with an example: gzip foo. Want to remove foo and create the compressed version of foo, create foo.gz. If interrupted, no foo.gz, keep foo(both or nothing). Here's how we do it:
- fd = open("foo", O_RDONLY)
- Create empty file foo.gz: fd = open("foo.gz", 0666);
- read from foo, write to foo.gz
- close(fd)
- unlink("foo") // saves a ton of disk space
Want remove and create to be a single complete operation. If the user types ctrl-c then the process will be undone.
-
We want processes to be able to deal with unexpected events. They are
exceptional events, and we assume that these events are rare.
-
When we read from the end of file, is EOF rare? if the file is large,
then yes, EOF is rare.
-
Responding to a signal should still be fast: Though it might not be as
fast as a system call.
-
Alternatives to system handling: We can arrange to have a special file
that contains special signals received, but this polling approach we have to
keep constantly checking.
The API for signal handling
-
user includes signal.h.
-
handler_t signal (int, handler_t)
- int = signal No. e.g. SIGPIPE
- handler_t: typedef void (*handler_t) (int)
-
Common way of defining and installing signal handling:
#include < signal.h >
int gotpipe;
void handle (int sig) { gotpipe == malloc(100); strcpy (gotpipe); } //You should be careful what system calls you use in this handler function. For example, don't call printf here or you will mess up the pointers
int main(void) {
old_handler = signal (SIGPIPE, handler) // With signal handling, can call from within signal call itself
malloc(3000);
}
-
Some examples of signals (defined in signal.h): SIGINT, SIGPWR, SIGHUP,
SIGTERM, SIGSGV, etc.
-
The kill() system call
int kill (pid_t, int): deliver the signal number(int) to process(pid_t).
Some other issues
-
Between step 4 and 5, we should introduce 4.5: If signint arrives, just exit, which is the default behavior of signal handler: signal(SIGINT, SIG_DFL);