Interprocess Communication: Signals and Pipes
1. Signals
1.1 Signal Environments
1.2 Experiments with signal()
Copyright By PowCoder代写 加微信 powcoder
1.3 Experiments with POSIX.1 sigaction()
2.1 Named Pipes (FIFOS)
3. Concurrent servers: A First Look 3.1 Example: POLL()
CMPUT 379 (E.S. Elmallah)
Files and pipes are two basic inter-process communication (IPC) facilities.
By default, pipes provide half-duplex ( uni-directional), reliable, flow-controlled, byte stream that can be established only between two related processes.
Implemented as sockets in BSD4.2 (previously, as files).
CMPUT 379 (E.S. Elmallah)
Use of descriptors :
o unlike files, pipes are not named objects, but like files
they are managed through a kernel descriptor table .
o recall that the BSD4.3 ’s kernel keeps a descriptor table for each process in the user structure (the u-dot structure)
o each entry in the table is indexed by a small integer, called a descriptor ; all I/O is done through such descriptors.
CMPUT 379 (E.S. Elmallah)
Digression: Unix provides two families of I/O functions o Low Level I/O (aka unbuffered I/O)
• each read or write invokes a system call to the kernel
• not included in ANSI-C
• open (), creat(), close(), lseek(), read(), write(), etc.
• performance is sensitive to “buffsize”
o High Level Stream I/O (the Standard I/O Library)
• included in ANSI-C
• setbuf(), fflush(), fopen(), fclose(), getc(), gets(), etc. • handles buffer allocation in optimized I/O chunks
o There are alternatives to the Standard I/O Library: e.g., [Korn and Vo’91] sfio library.
CMPUT 379 (E.S. Elmallah)
a process obtains a descriptor either by opening an object, or by inheritance from the parent process.
o By default: 0= stdin , 1= stdout , and 2= stderr .
CMPUT 379 (E.S. Elmallah)
I/O Redirection:
o we are allowed to close the stdin, stdout, or stderr, and reallocate the corresponding descriptor to some other file (or pipe): e.g.,
close(0); open(“/tmp/myfile”, O RDONLY, 0);
Note: open() uses the lowest available descriptor.
o we can also create a second reference to an existing
descriptor: e.g., if oldfd already exists then
dup2(oldfd, newfd); close(oldfd);
replaces oldfd with newfd, without closing the object referenced by oldfd
CMPUT 379 (E.S. Elmallah)
Creating a Pipe:
o Simply use int fd[2]; pipe(fd); – two descriptors are
opened: fd[0] for reading, and fd[1] for writing.
o A read (after closing fd[1], and the pipe empties)
returns 0 (end of file).
o A write (after closing fd[0] ) generates SIGPIPE to the
o Multiple writers: PIPE_BUF specifies the maximum amount of data that can be written atomically.
CMPUT 379 (E.S. Elmallah)
Example: simulate a pipeline: who | cat
#include
// POSIX.2 Symbolic Constants
#define MAXBUF 128
int main (int argc, char *argv[]) { char buf[MAXBUF];
int n, status, fd[2];
pid_t pid;
if (pipe(fd) < 0) perror("pipe error!"); if ((pid= fork()) < 0) perror("fork error!");
CMPUT 379 (E.S. Elmallah)
if (pid == 0) {
close(fd[0]); // child won’t read dup2 (fd[1], STDOUT_FILENO); //stdout= fd[1]
HH close(fd[1]); // stdout is still open if (execl("/usr/bin/w", "w", (char *) 0) < 0)
perror("execl error!"); } else {
close(fd[1]); // parent won’t write while ((n= read(fd[0], buf, MAXBUF)) > 0)
write(STDOUT_FILENO, buf, n); close(fd[0]);
waitpid(pid, &status, 0); // better than wait(&status) }
return 0; }
CMPUT 379 (E.S. Elmallah)
A useful setup:
• Forks a shell, passes the cmd_string to the shell, and returns a standard I/O file pointer.
• If type = “r” (“w”), the file pointer is connected to stdout (stdin) of the cmd_string
• Closes the standard I/O stream
• Waits for cmd_string to finish
• Returns the termination status of the shell
CMPUT 379 (E.S. Elmallah)
#include
FILE *popen (const char *cmd_string, const char *type); int pclose (FILE *fp);
2.1 Named Pipes (FIFOS)
Like pipes, FIFOs are half-duplex channels, managed by the descriptor table (in fact, may be implemented as special files).
FIFOs provide IPC between unrelated processes: possibly multiple writers (synchronization?), and a single reader .
Creation :
#include
#include
int mkfifo (const char *path, mode_t mode);
CMPUT 379 (E.S. Elmallah)
Access: using file operations (e.g., open() , read() , write() , etc.)
By default, an open-for-read (with no corresponding open-for-write ) blocks a process.
A write (with no open-for-read) generates SIGPIPE .
CMPUT 379 (E.S. Elmallah)
3. Concurrent servers: A First Look
an iterative server serves one client to completion before switching to another client.
a concurrent server uses time division multiplexing to serve a number of clients.
Q: How does one design a concurrent server? To start, here is a simple framework
o at any time, the server knows N (the # of clients), and the file descriptors fd[0], fd[1], …, fd[N-1] used in the communications
o listening to a client, and reading the incoming requests can be done using some kind of read(fd[i]) operation
CMPUT 379 (E.S. Elmallah)
Q: What are the possible design options?
o fork N copies of the server; assign each copy to a
specified client.
Q: What if the server needs to collect data sent from all clients in a shared buffer (e.g., X Windows updates the display from (possibly remote) clients)?
o Assign a thread of control to each client. • Q: Is it OK to use out-of-kernel threads?
CMPUT 379 (E.S. Elmallah)
Repeatedly poll the file descriptors using a (high level) Standard I/O Library function (e.g., fread()), or even a low-level I/O function (e.g., read()):
#include
int main (int argc, char *argv[]) {
int fd[4], buffsize= 100, buff[buffsize]; ssize_t length;
fd[0]= STDIN_FILENO;
length= read(fd[0], buff, buffsize); …
No: typically (and by default) such a function blocks until it gets some input.
CMPUT 379 (E.S. Elmallah)
Use nonblocking I/O in the polling loop (see [SR 3/E] Chapter 3]: e.g.,
#include <...>
#include
int fcntl (int FILEDS, int CMD, /* int ARG */ …);
o FILEDS: a file descriptor to operate on
o CMD: one (or more) operation(s) on the descriptor, or its associated flags
o ARG: argument(s) specific to the CMD
CMPUT 379 (E.S. Elmallah)
#include
int fd[4], buffsize= 100, buff[buffsize]; ssize_t length;
fd[0]= STDIN_FILENO;
fcntl(fd[0], F_SETFL, O_NONBLOCK); …
// loop to poll several file descriptors length= read(fd[0], buff, buffsize);
printf (“length= %d, errno= %d\n”, length, errno);
CMPUT 379 (E.S. Elmallah)
o Here, read() returns -1, with errno ≥ 0 if the source
has no data.
o wastes CPU time
o to improve performance, we may have to guess how long to wait each time around the loop
CMPUT 379 (E.S. Elmallah)
Use asynchronous I/O: let the sender(s) notify the receiver of incoming data using signals.
o Note: POSIX provides asynchronous I/O functions: aio_read(), aio_write(), etc. ; however, not all systems support this feature (see [SR 3/E] Section 14.5])
o If we enable more than one descriptor for asynchronous I/O on one signal, we cannot tell which descriptor the signal corresponds to.
o multiple signals of the same type can be lost o there is a limited number of signals
CMPUT 379 (E.S. Elmallah)
Use kernel-supported I/O multiplexing (see [SR 3/E] Section 14.4])
#include
o FDS[] is an array of pollfd structures; each member defines a descriptor, and the conditions that we are interested in
struct pollfd {
int short short
fd; events; revents;
//file descriptor
// requested events
// returned events
CMPUT 379 (E.S. Elmallah)
o Examples of events and revents flags include: POLLIN (= 0x0001), POLLOUT (= 0x0004), POLLRDNORM (= 0x0040), etc.
o Examples of revents (only) flags include: POLLERR (= 0x0008) (error occurred), POLLHUP (= 0x0010) (hangup; no writing, but may have more data to read), etc.
o TIMEOUT (in msec): TIMEOUT = 0 (no waiting), TIMEOUT > 0 (wait for a while), TIMEOUT= INFTIM (block on poll)
CMPUT 379 (E.S. Elmallah)
3.1 Example: POLL()
The main process (a read-only server) forks N children (write-only clients), and communicates via pipes
Each client loops a number of times writing a unique ’id’ to a specific pipe, and then sleeps for a while. At the end, the client closes the connection (sends ’-1’).
#include
#define MAXBUF 80
int fd[N][2], pid[N];
CMPUT 379 (E.S. Elmallah)
int fork_and_write (int id) { int i;
char buf[1];
pid_t pid;
if ( (pid= fork()) < 0 ) { perror ("fork error \n"); exit (1); } if (pid > 0) return pid;
buf[0]= ’0’ + id;
close (fd[id][0]);
for (i=0; i < 10; i++) {
if (write(fd[id][1], buf, 1) < 0)
{ perror ("pipe write error \n"); exit (1); }
sleep(1); }
buf[0]= -1; write(fd[id][1], buf, 1); exit (0);
CMPUT 379 (E.S. Elmallah)
// child won’t read
The server (read-only) maintains a done flag for each client. The main loop polls each client, and concurrently does something else (writes a dot)
int main (int argc, char *argv[]) {
struct pollfd
i, len, rval, writerId, n_writers; dotCount, timeout, done[N];
buf[MAXBUF]; pollfd[N];
for (i= 0; i < N; i++)
if (pipe(fd[i]) < 0) { perror ("pipe error \n"); exit (1); }
for (i= 0; i < N; i++) pid[i]= fork_and_write(i);
CMPUT 379 (E.S. Elmallah)
for (i= 0; i < N; i++) {
close (fd[i][1]); //parent won’t write done[i]= 0;
pollfd[i].fd= fd[i][0];
pollfd[i].events= POLLIN;
pollfd[i].revents= 0;
timeout= 0; n_writers= N; dotCount= 0;
CMPUT 379 (E.S. Elmallah)
while (n_writers > 0) {
rval= poll (pollfd, N, timeout);
for (writerId= 0; writerId < N; writerId++) {
if ( done[writerId] == 0 && (pollfd[writerId].revents & POLLIN) ) {
len= read (fd[writerId][0], buf, MAXBUF); for (i= 0; i < len; i++) {
if (buf[i] == -1) { n_writers--; done[writerId]= 1; break; }
else printf("[%c] ", buf[0]); }
if ( ++dotCount % 10000 == 0 ) putc(’.’, stderr); }
printf("\n"); }
CMPUT 379 (E.S. Elmallah)
Exercises
o In main(), what if ’close (fd[i][1])’ is moved to the
second loop?
o What if we eliminate the done[] flags?
CMPUT 379 (E.S. Elmallah)
程序代写 CS代考 加微信: powcoder QQ: 1823890830 Email: powcoder@163.com