Midterm Examination #1 COP 4610 - Operating Systems

This is a closed-book examination. You are not allowed to use any notes or other references during the exam. You will have 50 minutes to answer all the questions. Answer the true/false and short-answer questions on this paper. Answer the long-answer questions on the paper provided to you. There are 12 numbered questions, each worth 10 points, but you will probably find some questions are easier for you than others. Therefore, it is important to budget your time. Do not miss the chance to answer some easier questions by puzzling too long over a question that is giving you difficulty. Remember to write your name on every page, and turn in all pages of the exam.

  1. Answer each question by marking a "T" or "F" next to the statement, inside the parentheses on the left.
    1. (T) When pthread_cond_signal(&C) is called, if there are one or more threads waiting on C one or more of the threads will become unblocked.
    2. (T) If a process performs a signal operation on a counting semaphore whose value is zero, and there are one or more other processes waiting on the semaphore, exactly one of the other processes will be unblocked.
    3. (T) If a thread has a signal blocked (and the action for this signal has not been set to ignore) when the signal is generated for the thread, and then the thread unblocks the signal, if the process has a handler installed the handler will execute at least once.
    4. (T) Monitors are a programming language construct, and so their implementation requires support from a compiler.
    5. (T) An advantage of monitors over calls to synchronization primitives such as semaphores is that they prevent the programming error of forgetting to release a lock.
    6. (T) An open file descriptor in a Unix process is an integer.
    7. (T) Two Unix processes may simultaneously have open file descriptors that refer to the same open file description.
    8. (T) Two open file descriptions in the same Unix processes may refer to the same file.
    9. (F) When a file is closed the corresponding open file description is deallocated.

      It is not deallocated until the "last close". That is, until the count of references from file descriptors of processes to the open file description goes to zero.

    10. (T) Unix uses reference counts to keep track of references to files.

      This is also true for open file descriptions.

  2. Answer each question by marking a "T" or "F" next to the statement, inside the parentheses on the left.
    1. (F)Thread creation and thread synchronization can be done with less execution time overhead if threads are implemented at the kernel level.

      Kernel traps have more execution time overhead than function calls.

    2. (T)Threads that are implemented at the kernel level are likely to be more able to take advantage of an SMP system than threads that are implemented only at the library/user level.
    3. (F)When a thread makes a system call it is more likely to block the entire process if the threads are implemented at the kernel level than if they are implemented at the library/user level.

      The reverse is true. Reducing process-level blocking is one of the main reasons for putting the thread implementation into the OS kernel.

    4. (T)All the threads in a process share access to the same set of open files (i.e., the same mapping of open file descriptors to open file descriptions).
    5. (T)All the threads in a process share access to the same virtual memory space.
    6. (T)In the process models described by Stallings, whether a process is blocked or not is independent of whether it is suspended or not.
    7. (F)In the process models described by Stallings, a process that is swapped out of main memory would be in a blocked state.

      This was his first example of the suspended state.

    8. (T)When a thread calls pthread_cond_wait(&C, &M) the effect includes atomically unlocking M.
    9. (T)After return from a call to pthread_cond_wait(&C, &M) the calling thread can rely that the mutex M is locked.
    10. (F)A reentrant procedure is coded in the C language by using static local variables.

      Static local variables are sure to make the procedure non-reentrant.

  3. What is the effect of the Unix kill() system call?

    To send a specified signal to a process or a set of processes.

  4. Describe the possible effects on a Unix process of receiving a signal, such as SIGALRM.

    For most signals, including SIGALRM the default signal action is to terminate the process. That is the effect if there is no handler installed for the signal. If a handler is installed, the effect of the signal is to cause the handler to be executed by the process, interrupting whatever the process is doing at the time. (There are more details and special cases, but that is enough to earn full credit for this question.)

  5. What are the three main disadvantages/problems with using a machine instruction to implement mutual exclusion, e.g., using a test-and-set instruction to busy-wait on a lock variable?

    1. busy-waiting wastes processor time

    2. starvation is possible

    3. if the processor can be preempted (in response to an interrupt) during a critical section, we have deadlock

  6. Give four examples of information that ordinarily would be saved in a process control block, but not in a thread control block.

    1. process id

    2. reference/link to parent process

    3. reference/link to set of child processes

    4. reference/link to set of threads

    5. signal handler/action mapping table

    6. information needed to manage virtual memory

    7. etc.

  7. Explain what is meant by starvation in the context of a solution to the mutual exclusion problem.

    A condition in which a process is indefinitely delayed because other processes are always given preference. This is distinguished from deadlock by the fact that all the resources continue to be granted and released. The problem is that they are always going to other processes.

  8. Suppose there is a (non-virtual memory) system that has only 200 Mb of main memory and there are two processes that make the following sequence of requests:

    P1            P2
    ...   ...
    Request 80Mb;   Request 70Mb;
    ...   ...
    Request 60Mb;   Request 80Mb;

    How could a system avoid this kind of deadlock?

    The system should apply the Banker's Algorithm, which would have the effect of delaying granting the first request of one of the processes until the other process has completed and released its resources. A complete answer would show how the algorithm would avoid the deadlock.

  9. Is it possible for the two threads to deadlock by concurrent calls to the functions A and B below? If so, give an example of an execution trace that leads up to deadlock. If not, explain why deadlock is impossible, appealing to the four necessary conditions for deadlock in Chapter 6. Answer on a separate sheet of paper.

    void A (){
       ...
       pthread_mutex_lock (&M2);
       pthread_mutex_lock (&M1);
       ... critical section ...
       pthread_mutex_unlock (&M1);
       pthread_mutex_unlock (&M2);
    }
    void B (){
       ...
       pthread_mutex_lock (&M1);
       pthread_mutex_lock (&M2);
       ... critical section ...
       pthread_mutex_unlock (&M2);
       pthread_mutex_unlock (&M1);
    }
    

    There is a deadlock if the two processes execute in the following interleaved order:

    Apthread_mutex_lock (&M2);succeeds
    Bpthread_mutex_lock (&M1);succeeds
    Apthread_mutex_lock (&M1);blocks
    Bpthread_mutex_lock (&M2);blocks

  10. What is the output of the following program? Answer by writing on this page.

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    
    int main () {
        int n, status;
        pid_t child;
        n = 0;
        if (!(child = fork())) {
            n = 1;
            fprintf (stderr, "n = %d\n", n);
            n = 2;
        } else {
            n = 3;
            fprintf (stderr, "n = %d\n", n);
            n = 4;
            exit (0);
            fprintf (stderr, "n = %d\n", n);
        }
        waitpid (child, &status, 0);     
        n = 5;
        fprintf (stderr, "n = %d\n", n);
        return 0;
    }

    This program is similar-looking to the one in the sample examination, but important details have been changed. This program reflects what would happen if a person made two typical programming errors:

    1. mixing up the parent and child cases in the if-statement
    2. not checking the return value of the waitpid() call

    Three different output sequences are possible, depending on how the system schedules processes. The most likely scheduling is for the system to allow the parent process to continue until it blocks, and then execute the child process. That would result in the following output:

    n = 3
    n = 1
    n = 5
  11. The following code is intended to implement one of the operations on a counting semaphore.

    pthread_mutex_lock (&S.mut);
    while (S.count == 0) pthread_cond_wait (&S.cond, &S.mut);
    S.count --;
    pthread_mutex_unlock (&S.mut);

    The name of the operation implemented above is ___wait___

    The other operation can be implemented as follows:

    
    pthread_mutex_lock (&S.mut);
    S.count++;
    pthread_cond_signal (&S.cond);
    pthread_mutex_unlock (&S.mut);
    
  12. Explain how a lockfile may be used to implement mutual exclusion between two processes. Your answer should include pseudo-code for the lock and unlock operations. Answer on separate paper.

    Before entering a critical section, each process tries to create a lockfile, using the same pathname for the file. If a process fails to create the file, it sleeps a short time and then retries. Once a process has created the lockfile it enters the critical section. On leaving the critical section, a process unlinks the lockfile.

    lock:
            while (attempt to create lockfile fails) {
               sleep a short time;
            }
                
    unlock: 
            unlink lockfile;
    

T. P. Baker