Name: ___Answers______________________ Login: ___couch_____

Comp111 Midterm Exam
Oct 23, 2012 - open books and notes

No electronic devices of any kind allowed, including cellphones, calculators, pagers, etc. Please turn these off before beginning the exam.

Please show all work on these pages. Make sure that your work for each problem is clearly marked. These pages will be graded and returned electronically, like exercises.

  1. (20 points) Consider the following code:
     
    main()
    { 
        FILE *foo = fopen("foo.txt", "w"); 
        fprintf(foo, "yo there"); 
        close(1); dup(fileno(foo)); 
        if (fork()) { 
            fprintf(foo, "hi there\n"); 
        } else { 
    	printf("ho there\n"); 
        } 
    } 
    
    After this code fragment, what is printed to the user, and what are the possible contents of "foo.txt"? Why?
    Answer: This code has a buffering botch followed by a race condition. It is very similar to a class example:
    1. First "yo there" is placed into the buffer for file pointer foo but not flushed.
    2. Then stdout is pointed at "foo.txt". Thus, there are now two active buffers.
    3. Then the two following things happen in an undetermined order due to a race condition:
      • In the parent, printing "hi there\n" to foo flushes the foo buffer and prints "yo therehi there\n" to foo.txt.
      • In the child, printing "ho there\n" to standard output (surprisingly) does an implicit flush of foo's buffer first, and prints "yo thereho there\n" to foo.txt. This surprised even me. I thought it would print "hothere\nyo there" (due to an implicit flush at exit).
      So, at the end of everything:
      • foo.txt contains "yo therehi there\n" and "yo thereho there\n". in some order.
      • Nothing is printed on standard output.
    You can verify this by running midterm.c.

    All other outcomes are prohibited by atomicity of the write system call during flush.

    Because my answer turned out to be incorrect, I will accept either my answer or the actual answer!

  2. Suppose that two processes try to execute the following fragments:
    pthread_mutex_lock(&A); 
    pthread_mutex_lock(&C); 
    pthread_mutex_lock(&D); 
    pthread_mutex_lock(&E); 
    
    pthread_mutex_lock(&B); 
    pthread_mutex_lock(&D); 
    pthread_mutex_lock(&C); 
    pthread_mutex_lock(&E); 
    
    where A, B, C, D, E are mutexes.
    1. (10 points) Can these two fragments deadlock? If so, give a schedule that deadlocks. If not, why not?
      Answer: Any schedule in which the left process locks C before the right process locks D results in a crossbar deadlock, as discussed in class.
    2. (10 points) Can the order in which mutexes are unlocked ever cause a deadlock? Why or why not?
      Answer: Unlocking cannot cause a deadlock because it always succeeds and never blocks.
    3. (10 points) Suppose we implement the above with semaphores and one call to semop per thread, rather than with multiple calls to pthread_mutex_lock. Can the resulting fragments deadlock? Why or why not?
      Answer: As discussed in class, the atomicity of semop precludes deadlock by acquiring all four locks at the same time.
  3. Consider the code:
     
    main()
    {
        if (fork()) {
            int status; wait(&status);
        } else {
            int fd1 = open("foo.txt", O_WRONLY, 0);
            int fd2 = open("bar.txt", O_RDONLY, 0); 
            close(0); dup(fd1);
            close(1); dup(fd2);
            execl("/bin/cat", "/bin/cat", NULL);
        }  
    }
    
    1. (10 points) This code contains a rather obvious error that prevents it from doing anything useful. What is it? How would you correct it?
      Answer: The file pointers stdout and stdin are duped to file descriptors that are opened in the wrong directions. One can invert the order of the closes or the order of the dups and all will be well.
    2. (10 points) With the error corrected, what does the code do?
      Answer: Depending on how you repaired the dup problem, the resulting code is equivalent to the shell command
       
      /bin/cat < foo.txt > bar.txt
      
      or
       
      /bin/cat < bar.txt > foo.txt
      
      In other words, it copies one file over another.
    3. (10 points) In my examples, I often omitted calls to close. Which ones did I omit here? Why is this not an error?
      Answer: Technically, I should have closed fd1 and fd2 after duping them. This has no consequence because the cat command ignores the fact that they are open.
  4. (20 points) Suppose I have written a threaded routine
     
    struct output *doit(struct input *stuff); 
    
    How does one have to invoke this thread inside pthread_create, and why?
    Answer: It is necessary to strip the types off of the function to make the function type-conformal with the pthread_create call, to wit:
     
    pthread_t thread; 
    struct input in;
    pthread_create(&thread, NULL, (void *(*)(void *)) doit, (void *)&in); 
    
  5. (Advanced; 10 points extra credit) List the things that a process does not need to know about the operating system, and explain why that knowledge is not necessary.
    Answer: A process can determine nearly nothing about the operating system. In particular, the details of scheduling, paging, resource management, etc are unknown to the process.