Team BBL
Previous Page Next Page

Chapter 15

15.1

If the write end of the pipe is never closed, the reader never sees an end of file. The pager program blocks forever reading from its standard input.

15.2

The parent terminates right after writing the last line to the pipe. The read end of the pipe is automatically closed when the parent terminates. But the parent is probably running ahead of the child by one pipe buffer, since the child (the pager program) is waiting for us to look at a page of output. If we're running a shell, such as the Korn shell, with interactive command-line editing enabled, the shell probably changes the terminal mode when our parent terminates and the shell prints a prompt. This undoubtedly interferes with the pager program, which has also modified the terminal mode. (Most pager programs set the terminal to noncanonical mode when awaiting input to proceed to the next page.)

15.3

The popen function returns a file pointer because the shell is executed. But the shell can't execute the nonexistent command, so it prints

      sh: line 1: ./a.out: No such file or directory

on the standard error and terminates with an exit status of 127. pclose returns the termination status of the command as it is returned by waitpid.

15.4

When the parent terminates, look at its termination status with the shell. For the Bourne shell, Bourne-again shell, and Korn shell, the command is echo $?. The number printed is 128 plus the signal number.

15.5

First add the declaration

   FILE   *fpin, *fpout;

Then use fdopen to associate the pipe descriptors with a standard I/O stream, and set the streams to be line buffered. Do this before the while loop that reads from standard input:

    if ((fpin = fdopen(fd2[0], "r")) == NULL)
        err_sys("fdopen error");
    if ((fpout = fdopen(fd1[1], "w")) == NULL)
        err_sys("fdopen error");
    if (setvbuf(fpin, NULL, _IOLBF, 0) < 0)
        err_sys("setvbuf error");
    if (setvbuf(fpout, NULL, _IOLBF, 0) < 0)
        err_sys("setvbuf error");

The write and read in the while loop are replaced with

     if (fputs(line, fpout) == EOF)
         err_sys("fputs error to pipe");
     if (fgets(line, MAXLINE, fpin) == NULL) {
         err_msg("child closed pipe");
         break;
     }

15.6

The system function calls wait, and the first child to terminate is the child generated by popen. Since that's not the child that system created, it calls wait again and blocks until the sleep is done. Then system returns. When pclose calls wait, an error is returned, since there are no more children to wait for. Then pclose returns an error.

15.7

The select function indicates that the descriptor is readable. When we call read after all the data has been read, it returns 0 to indicate the end of file. But with poll (assuming a STREAMS-based pipe), the POLLHUP event is returned, and this event may be returned while there is still data to be read. Once we have read all the data, however, read returns 0 to indicate the end of file. After all the data has been read, the POLLIN event is not returned, even though we need to issue a read to receive the end-of-file notification (the return of 0).

With an output descriptor that refers to a pipe that has been closed by the reader, select indicates that the descriptor is writable. But when we call write, the SIGPIPE signal is generated. If we either ignore this signal or return from its signal handler, write returns an error of EPIPE. With poll, however, if the pipe is STREAMS based, poll returns with a POLLHUP event for the descriptor.

15.8

Anything written by the child to standard error appears wherever the parent's standard error would appear. To send standard error back to the parent, include the shell redirection 2>&1 in the cmdstring.

15.9

The popen function forks a child, and the child executes the shell. The shell in turn calls fork, and the child of the shell executes the command string. When cmdstring terminates, the shell is waiting for this to happen. The shell then exits, which is what the waitpid in pclose is waiting for.

15.10

The trick is to open the FIFO twice: once for reading and once for writing. We never use the descriptor that is opened for writing, but leaving that descriptor open prevents an end of file from being generated when the number of clients goes from 1 to 0. Opening the FIFO twice requires some care, as a nonblocking open is required. We have to do a nonblocking, read-only open first, followed by a blocking open for write-only. (If we tried a nonblocking open for write-only first, it would return an error.) We then turn off nonblocking for the read descriptor. Figure C.18 shows the code for this.

Figure C.18. Opening a FIFO for reading and writing, without blocking
#include "apue.h"
#include <fcntl.h>

#define FIFO    "temp.fifo"

int
main(void)
{
    int    fdread, fdwrite;

    unlink(FIFO);
    if (mkfifo(FIFO, FILE_MODE) < 0)
        err_sys("mkfifo error");
    if ((fdread = open(FIFO, O_RDONLY | O_NONBLOCK)) < 0)
        err_sys("open error for reading");
    if ((fdwrite = open(FIFO, O_WRONLY)) < 0)
        err_sys("open error for writing");
    clr_fl(fdread, O_NONBLOCK);
    exit(0);
}

15.11

Randomly reading a message from an active queue would interfere with the clientserver protocol, as either a client request or a server's response would be lost. To read the queue, all that is needed is for the process to know the identifier for the queue and for the queue to allow world-read access.

15.13

We never store actual addresses in a shared memory segment, since it's possible for the server and all the clients to attach the segment at different addresses. Instead, when a linked list is built in a shared memory segment, the list pointers should be stored as offsets to other objects in the shared memory segment. These offsets are formed by subtracting the start of the shared memory segment from the actual address of the object.

15.14

Figure C.19 shows the relevant events.

figure C.19. Alternation between parent and child in Figure 15.33

Parent i set to

Child i set to

Shared value set to

update returns

Comment

  

0

 

initialized by mmap

 

1

  

child runs first, then is blocked 0 parent runs

0

   

parent runs

  

1

  
   

0

then parent is blocked

  

2

 

child resumes

   

1

 
 

3

  

then child is blocked

2

   

parent resumes

  

3

  
   

2

then parent is blocked

  

4

  
   

3

 
 

5

  

then child is blocked

4

   

parent resumes


    Team BBL
    Previous Page Next Page