CS413: Fall 1997 Midterm Exam


Take your time and read each question carefully. Some have
multiple parts. Be sure to answer each part. 
Also, be sure to put your name on each exam page.


1. Fill in the blanks in the following short statements. (20 pts)

(a) A program runs in [ protected ] mode when it executes protected instructions.
(b) [ save registers ] and [ restore registers ] are the first and last 
    things that are executed during an interrupt service.
(c) [ non-preemptive ] scheduling switches from one process to another only when
    the running process terminates or blocks. In contrast [ preemptive ] scheduling
    uses a clock interrupt to switch from one process to another after a time slice.
(d) These are four of the contents of the PCB: [ PC, registers ], 
    [ memory management information ], [ accounting information ], and 
    [ I/O status ].
(e) Most of the tasks in a computer are [ I/O ] bound.
(f) Predicted SJF uses [ past behavior ] to estimate the length of the next 
    processing burst.
(g) [ starvation ] is a condition that happens when a process waits for something
    that may never happen.

2. Consider the following C functions and data declarations: (20 pts)

struct S {
   int data;
   S * last;
} ListElement;

struct ListElement * list;

  sema_t  sem = 1;        /* solution  */     

void
Enq( ListElement * newElement )
{
     p(sem);          /* solution */  

     newElement->next = list;   /* STATEMENT 1 */


     list = newElement;         /* STATEMENT 2 */
     v(sem) ;        /* solution */   

}

ListElement *
Deq()
{
     ListElement * returnValue;
      p(sem) ;       /* solution */          
     returnValue = list;        /* STATEMENT 3 */


     list = list->next;         /* STATEMENT 4 */
      v(sem)        /* solution */              

     return( returnValue );
}

Answer the questions on the following page by assuming that
an invocation of Enq() and a simultaneous invocation of Deq() are
concurrently executing on two processors in a multiprocessor. 

(a)  List all the possible execution orders in time of the 4 statements
     labeled. 

          1  3  4  2
          1  3  2  4
          3  4  1  2 
          3  1  2  4 
          3  1  4  2


(b)  Pick one of these execution orders which will cause the code to not behave
     as obviously intended (e.g., the list gets corrupted) and describe the actual
     outcome.


     we pick the order    1  3  4  2
     after the execution, the returnValue in dequeue points to an entry in the 
     middle of the list.


(c) Using a single shared semaphore, fix the example to work as intended.
    (remember to Initialize you semaphore). Show your changes directly on the code
    on the last page.

      see the above program.  


3. Assume that two or more processes communicate with each other
using  enqueue  and  dequeue (described below) on
a circular queue. Complete these procedures using semaphores.
Make sure that a processes calling  enqueue will block if the queue
is full, and a process calling  dequeue will block if the queue is
empty. Also make sure that no races can happen. Don't forget the
semaphore initializations. (20 pts)

     

const int MAXQUEUE = 10;
int queue[ MAXQUEUE ];
int tail;
int head;

/* Other declarations and initializations here */


    sema_t semfull = 10;
    sema_t semempty = 0 ;
    mutex_t  mutex = 1;



void enqueue( int a )
{
   
    p(semfull);
    p(mutex);


    queue[ tail] = a;
    tail = (tail + 1)%MAXQUEUE;


    v(mutex);
    v(semempty);


}

int dequeue()
{


    p(semempty);
    p(mutex); 
   

    tmp = queue[ head ];
    head = (head + 1)% MAXQUEUE;


    v(mutex);
    v(semfull);

}


4.  Complete the following procedures. sendBroadcast sends a message to N 
    processes that will call  receiveBroadcast. sendBroadcast and  receiveBroadcast
    will block until the message has been delivered to the N processes.
    Don't forget to initialize the semaphores. (20 pts)

const int N = 5;
int theMessage;

/* Other declarations and initializations here */


   sema_t semMessageSent = 0;
   sema_t semAllReceived = 0;
   mutex_t mutex = 1;
   int num;


void sendBroadcast( int msg )
{
		
    theMessage = msg;


    for (int i = 0; i < N; i++){
     v(semMessageSent);
    }
    p(semAllreceived);


}


int receiveBroadcast()
{


    p(semMessageSent);   

    int tmp = theMessage;


    p(mutex);
    num++;
    if (num == N){
     for (int i = 0; i < N+1 ; i++){
       v(semAllRecieved);
     }
   }
   v(mutex);
   p(semAllreceived);



    return tmp;
}


5. Write a procedure redirectStdoutStderr that will redirect
both standard output and standard error to a single specified file.
Do not include code for error checking. (10 pts)


void redirectStdinStderr( char* fileName )
{
    /* Redirect standard output and standard error to fileName */


     int f = open(filename, O_RDWR);
     dup2(f, 1);
     dup2(f, 2);

     
}


6.  Write a procedure redirectOutputToGrep that will redirect
the current  standard output to a child process executing
 grep pattern  using a pipe. Do not include code for error checking. (10 pts)


void redirectOutputToGrep( char * pattern )
{
    /* Redirect standard output to a child executing  */
    /* 'grep pattern' using a pipe */


   int fd[2];
   
   pipe(fd);
   
   int pid = fork();
   if (pid == 0) {
    dup2(fd[0], 0);
    close(fd[0);
    close(fd[1]);
    exec("grep", pattern, 0);
   }

   dup2(fd[1],1);
   close(fd[0]);
   close(fd[1]);
}



}