Assignment 12, Solutions

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

  1. Background: In unix, interprocess communications pipes are created using the pipe() system call. This creates a new pipe, a FIFO communications channel, and returns two file descriptors, one referring to the read end of the pipe, the other referring to the write end. (Use man 2 pipe for more detail.) Of course, the new pipe only known to one process, the one that created it, but after creating the pipe, that process may fork into multiple processes.

    See man 2 dup for mechanisms permitting open files to be moved to a different descriptor number, and note that if an open file is visible from two descriptor numbers, close() applied to one of its numbers does not close access to the file through the other number.

    A problem: Give a fragment of C code that launches two applications using execve(), call them "app1" and "app2", where standard output of "app1" is piped into standard input of "app2", while both applications remain able to read and write the other files that were open prior to your code fragment. Your fragment should wait for both child processes to terminate before it exits. (1.0 points)

    {
            int fd[2];
            const int readend = 0; /* the two ends of the pipe in fd */
            const int writeend = 1;
            const int standardin = 0; /* the files we really want to change */
            const int standardout = 1;
    	pipe( fd );
    	if (fork() == 0) { /* child 1 */
                    dup2( fd[writeend], standardout );
                    close( fd[readend] );
                    close( fd[writeend] );
    		execve( "app1" ); /* standard output to the pipe */
            } else if (fork() == 0) { /* child 2 */
                    close( standardin );
                    dup( fd[readend] ); /* guaranteed to return standardin */
                    close( fd[readend] );
                    close( fd[writeend] );
    		execve( "app2" ); /* standard input from the pipe */
            } else { /* parent */
                    close( fd[readend] );
                    close( fd[writeend] );
    		wait();
    		wait(); /* we wait for both, not knowing which ends first */
            }
    }
    

  2. Background: Consider a client-server architecture. Assume that messages are sent and received from buffers in memory, and that the sender and receiver must specufy the buffer and its length. The following primitives are available:

    A problem: Write the code for a server that implements a single semaphore, with initial (abstract) value zero and client-to-server messages "P" and "V" corresponding to the wait and signal operations (names chosen in part because they are of length 1). You may assume pre-existing library implementations of such things as stacks and queues with the usual operations. (1.0 points)

    {
            int count = 0;
            queue myqueue;
            initialize_queue( &myqueue );
            for (;;) { /* an infinite loop */
                    char operation = " ";
                    ID client = server_receive( &operation, 1 );
                    if (operation == "P") {
                            if (count > 0) {
                                    count = count - 1;
                                    /* send prompt reply to P */
                                    server_send( client, NULL, 0 );
                            } else {
                                    /* remember client for delayed reply to P */
                                    enqueue( &myqueue, client );
                            }
                    } else if (operation == "V") {
                            /* always send prompt reply to V */
                            server_send( client, NULL, 0 );
                            if (is_empty( &myqueue )) {
                                    count = count + 1;
                            } else {
                                    /* send delayed reply to P */
                                    client = dequeue( &myqueue );
                                    server_send( client, NULL, 0 );
                            }
                    } else {
                            /* ignore illegal operations */
                    }
            }
    }
    

  3. Background: In Unix, the resources available to a running program fall into three broad categories:

    In Amoeba and Demos, many resources that are statically accessible in systems modeled on Unix are, instead, passed in what can be thought of as analogous to the environment in Unix. Among the most important of these is the file system.

    a) What system data structure holds the envoronment of a Demos process? (0.5 points)

    The link table.

    b) What system data structure in Unix (or Linux) is analogous to the data structure you identified in part a. Note that this Unix data structure has a far narrower use than the use it has in Demos. (0.5 points)

    The open file table.