Assignment 10, due Apr 18

Part of the homework for 22C:112, Spring 2008
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

Always, on every assignment, please write your name legibly as it appears on your University ID and on the class list! All assignments will be due at the start of class on the day indicated (usually a Friday). The only exceptions to this rule will be by advance arrangement unless there is what insurance companies call "an act of God" - something outside your control. Homework must be turned in on paper and in class! Late work may be turned in to the teaching assistant's mailbox, but see the late work policy. Never push late work under someone's door!

  1. Background: The Unix fork() system call returns the process ID of the new process to the parent, and it returns zero to the child. The Unix exit() system call terminates a process with the indicated exit status. The Unix wait() system call blocks the caller until a child process terminates and then returns the process ID of the child. Further details can be found in the Unix programmer's reference manual (man 2 and in some cases man 3). There is also a waitpid() system call that waits for a specific child process to terminate.

    A problem: Write Unix code to do the actions A to J in the order given below:

          A
        /   \
      B       C
     / \     / \
    D   E   F   G
     \ /     \ /
      H       I
        \   /
          J
    

    Note that D and E must be completed before H, and F and G must be completed before I. The order ABDEHCFGIJ would be legal, as would ABCDEFGHIJ and ACGFIBEDHJ. This will take 3 forks and 3 waits, but it is important for the parent process to wait for specific children at the right time, and if some child terminates in an unexpected order, it is important for the parent to remember that this child has already terminated. (1.0 points).

  2. Background: The unix pipe(f) system call returns an array of two file descriptors, such that any data written on f[1] can later be read from f[0]. The pipe is really a FIFO bounded buffer with a capacity of around one disk sector, although the actual capacity is not documented. Pipes exist for the specific purpose of interprocess communication, so the normal way to use them is to create a pipe before a fork, and then have the resulting processes communicate through the pipe.

    a) Augment the following code so that statements B and C can communicate through a pipe. Specifically, statement B produces data that statement C consumes. Make sure you close any unneeded ends of the pipe, and make sure that all evidence of the pipe has been closed by the time the two logical threads of computation join:

    if (fork()) { /* parent */
            B;
            wait();
    } else { /* child */
            C;
            exit();
    }
    
    (1 point)

    b) Pipes were never intended for other purposes, but they can be abused in some interesting ways. Consider using a pipe as a semaphore, with the number of characters of data in the pipe used to indicate the count in the semaphore. Give implementations of the wait and signal operations using a pipe as the semaphore representaiton. (1 point)