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.