Look at Unix primitives. Also covered in Tanenbaum chapter on Unix.
How does one process communicate with another process?
Similar to hardware interrupt; processes interrupt each other through software operations. Important to realize that interrupts are asynchronous! Stops execution and then restarts.
Examples:
/* example in /cs/cs3013/public/example/signal.c */ #include <signal.h> int n; main(int argc, char **argv) { void InterruptHandler(), InitHandler(); n = 0; signal(SIGINT, InterruptHandler); /* signal 2 */ signal(SIGHUP, InitHandler); /* signal 1 */ while (1) { n++; sleep(1); } } void InterruptHandler() { printf("The current value of n is %d\n", n); exit(0); } void InitHandler() { printf("Resetting the value of n to zero\n"); n = 0; }
% cc -o signal signal.c % ./signal ./signal ^C (interrupt character) The current value of n is 3 % ./signal & [1] 20822 % kill -1 %1 Resetting the value of n to zero % kill -2 %1 The current value of n is 19 [1] Done ./signal
In Unix, a pipe is a unidirectional, stream communication abstraction. Show a picture!! One process writes to the ``write end'' of the pipe, and a second process reads from the ``read end'' of the pipe.
The command interpreter is responsible for setting up a pipe. For instance, upon entering:
% ls | morethe shell would:
A pipe consists of (keep using the same picture showing the pipe as a buffer)
Pipes unify input and output. When a process starts up, it inherits open file descriptors from its parent.
Thus, when a process reads from standard input, it doesn't know (or care!) whether it is reading from a file or from another process.
Likewise, output written to standard output might go to a terminal, a file, or another process.
System calls:
/* /cs/cs3013/public/example/pipe.c */ #define DATA "hello world" #define BUFFSIZE 1024 int rgfd[2]; /* file descriptors of streams */ /* NO ERROR CHECKING, ILLUSTRATION ONLY!!!!! */ main() { char sbBuf[BUFFSIZE]; pipe(rgfd); if (fork()) { /* parent, read from pipe */ close(rgfd[1]); /* close write end */ read(rgfd[0], sbBuf, BUFFSIZE); printf("-->%s\n", sbBuf); close(rgfd[0]); } else { /* child, write data to pipe */ close(rgfd[0]); /* close read end */ write(rgfd[1], DATA, sizeof(DATA)); close(rgfd[1]); exit(0); } }
For the following, which is the parent and which is the child? (parent should read from pipe so ``more'' is the parent process). Last to complete.
% ls | more
Look at example 1-14. Tanenbaum uses dup(oldd) to duplicate a file descriptor. Also have a dup2(oldd, newd), which explicitly gives the new file descriptor.