JMU
Sample Questions for Examination 2


  1. Carefully explain the difference between byte stream oriented and message oriented data transfer mechanisms.
  2. Give an example of a data transfer mechanism that "blocks".
  3. Compare and contrast "Process Persistence" and "Kernel Persistence".
  4. In what ways are signals similar to exceptions/interrupts?
  5. What is the purpose of sem_close()?
  6. List two things that can cause a thread to terminate.
  7. Explain the different between the terms explicitly reentrant and implicitly reentrant.
  8. Choose the best answer to each of the following:
    (1) _____ A signal is said to be pending:
    1. Before it is generated
    2. Between the time it is generated and delivered
    3. After it is delivered
    4. All of the above
    5. None of the above
    (2) _____ A signal may have which of the following dispositions?
    1. Ignore the signal
    2. Terminate the process
    3. Stop the process
    4. All of the above
    5. None of the above
    (3) _____ A function is reentrant if:
    1. Its execution can be interrupted and it can be called again (before the previous invocation is completed)
    2. Its instructions are invariant and, hence, can be shared across instances
    3. Both of the above
    4. Neither of the above
    (4) _____ When a pipe/FIFO is empty:
    1. The process will generate a segmentation fault
    2. A write will block
    3. A read will block
    4. All of the above
    5. None of the above
    (5) _____ sem_post():
    1. Attempts to decrease the value of a sempahore by 1
    2. Increases the value of a semaphore by 1
    3. Sets a semaphore to an arbitrary value
    4. Removes a semaphore and marks it for destruction
    5. None of the above
    (6) _____ Which of the following are shared by all of the threads in a process?
    1. Initialized data segment
    2. Program counter
    3. Stack pointer
    4. All of the above
    5. None of the above
  9. Indicate whether each of the following statements is true or false:
    (1) _____ Interrupt handlers can be interrupted by signals
    (2) _____ Pipes/FIFOs guarantee writes (up to a certain size) are atomic
    (3) _____ Threads are often preferred to processes because they are easier to use with signals
    (4) _____ To avoid wasting resources, one must either call pthread_join() or pthread_detach() for every thread
    (5) _____ All thread-safe functions are reentrant
  10. Consider the following program.
    volatile int should = 0;
    
    static void
    sighandler(int sig)
    {
      should = 1;
    }
    
    int
    main(void)
    {
      float rate, sales, tax;
      int   status;
      pid_t pid;
    
      rate   = 0.05;
      pid    = fork();
    
      signal(SIGUSR1, sighandler);
    
      switch(pid)
        {
        case -1:
          exit(1);
    
        case 0:
          if (should_tax())
            {
              // Add your code here
            }
          break;
    
        default:
          sales = total_sales();
          wait(&status);
          if (should) tax = sales * rate;
          else        tax = 0.0;
          printf("Tax: %5.2f\n", tax);
          break;
        }
    }
    1. What is the purpose of the call to wait()?
    2. Why isn't it necessary to use waitpid() in this case?
    3. What will be printed by this program if the value returned by total_sales() is 100.00?
    4. If should_tax() returns "true", this program is supposed to print Tax: 5.00. What will be printed if you replace the line // Add your code here with should = 1? Why?
    5. Replace the line //Add your code here with one or more statements that will make the program work correctly.
  11. The following program is supposed to print Task 1 before it prints Task 2.
    static void
    handle_continue(int sig)
    {
      // Nothing to do
    }
    
    int
    main(void)
    {
      // Setup the signal handler
      signal(SIGCONT, handle_continue);
    
      pid_t  pid = fork();
      switch(pid)
        {
        case -1:
          exit(1);
    
        case 0:
          printf("Task 1\n");
          kill(getppid(), SIGCONT);
          exit(0);
    
        default:
          pause();
          printf("Task 2\n");
          exit(0);
        }
    }
    
    1. What is the purpose of the call to pause()?
    2. When will the call to pause() return?
    3. Under what circumstances won't this program generate the correct output?
    4. What is the general term for the kind of defect contained in the program?
  12. In the following program:
    int
    main(void)
    {
      char  msg[20];
      int   fd_pipe[2];
      int   n, status;
      pid_t pid;
    
      pipe(fd_pipe);
      
      pid    = fork();
      switch(pid)
        {
        case -1:
          exit(1);
    
        case 0:
          close(fd_pipe[1]);
          read(fd_pipe[0], msg, 20);
          close(fd_pipe[0]);
          write(STDOUT_FILENO, msg, 20);
          exit(0);
    
        default:
          close(fd_pipe[0]);
          write(fd_pipe[1], "Because I said so!\n\0", 20); 
          close(fd_pipe[1]);
          wait(&status);
          exit(0);
        }
    }
    
    1. What will be printed (if anything)?
    2. Why does the child process include the statement close(fd_pipe[1]) before the call to read()?
    3. Why does the child process include the statement close(fd_pipe[0]) after the call to read()?
    4. Why does the array fd_pipe contain two elements? In other words, what are the two elements used for?
    5. Suppose the child process executes close(fd_pipe[1]) before the parent process executes write(fd_pipe[1], "Because I said so!\n\0", 20), will the call to write() fail? Why or why not?
  13. Consider the following program:
    int
    main(void)
    {
      pid_t  pid = fork();
      switch(pid)
        {
        case -1:
          exit(1);
    
        case 0:
          write(STDOUT_FILENO, "A1 ", 3);
          write(STDOUT_FILENO, "A2 ", 3);
          exit(0);
    
        default:
          write(STDOUT_FILENO, "B1 ", 3);
          write(STDOUT_FILENO, "B2 ", 3);
          exit(0);
        }
    }
    
    1. What are the possible outputs of this program?
    2. Why must A1 precede A2 and B1 precede B2? (Your answer must use the phrase "control flow", and must use it properly.)
    3. Use one or more semaphores to ensure that this program can only generate the outputs:
      A1 B1 A2 B2
      B1 A1 A2 B2
      A1 B1 B2 A2
      B1 A1 B2 A2
      in other words, so that (in addition to the orderings discussed above) A1 must precede B2 and B1 must precede A1.
  14. Consider the following program:
    static void
    *count(void *arg)
    {
      char *s;
      void *result;
    
      s = (char *)arg;
      result = (void *)(strlen(s));
      return result;
    }
    
    int
    main(void)
    {
      char       *s;
      int        i;
      long       len;
      pthread_t  helper[5];
      void       *result;
    
      s = "James Madison University";
      len = strlen(s);
      
      for (i=0; i<5; i++)
        {
          pthread_create(&helper[i], NULL, count, s+(i*2));
        }
    
      for (i=0; i<5; i++)
        {
          pthread_join(helper[i], &result);
          len += (long)result;
        }
    
      printf("Characters printed: %ld\n", len);
      exit(0);
    }
    1. How many threads will the process use, in total, when this program is executed?
    2. What is the maximum number of threads that will be concurrent when this program is executed?
    3. What will be printed by this program?
    4. Why can't this program call pthread_detach() rather than pthread_join()?
  15. What will be printed by the following program?
    int count;
    
    
    static void
    *thread_entry(void *arg)
    {
      count += 5;
      return NULL;
    }
    
    
    
    int
    main(void)
    {
      pthread_t thread1;
    
      count = 10;
      printf("Before: %d\n", count);
      pthread_create(&thread1, NULL, code_for_thread1, NULL);
      pthread_join(thread1, NULL);
      printf("After: %d\n", count);
      exit(0);
    }
    
  16. Write a program with a main thread and a two helper threads. The main thread must print "A", one helper thread must print "B", and the other helper thread must print "C". The letters can be printed in any order.
  17. The following program is supposed to "count" using two helper threads.
    static volatile int global_counter = 0;
    
    
    static void*
    counter(void *arg)
    {
      int   i, limit, local_counter;
    
      limit = *((int *) arg);
      for (i=0; i<limit; i++)
        {
          local_counter = global_counter;
          ++local_counter;
          global_counter = local_counter;
        }
      
      return NULL;
    }
    
    
    
    int
    main(int argc, char *argv[])
    {
      int        limit;
      pthread_t  thread_a, thread_b;
      
      if (argc <= 1) limit = 100;
      else           limit = atoi(argv[1]);
    
      // Start two threads counting
      pthread_create(&thread_a, NULL, counter, &limit);
      pthread_create(&thread_b, NULL, counter, &limit);
    
      // Wait for both threads to terminate
      pthread_join(thread_a, NULL);
      pthread_join(thread_b, NULL);
    
      // Display the result
      printf("Expected: %d\n", limit*2);
      printf("Actual:   %d\n", global_counter);
      exit(0);
    }
    

    but contains a defect known as a read-modify-write condition (a particular kind of race condition). Use one or more pthread_mutex_t variables to eliminate this defect.

  18. The following multi-threaded program contains a race condition.
    
    static int should = -1;         // Shared variable
    
    static void
    *thread_entry(void *arg)
    {
      if (should_tax()) should = 1;
      else              should = 0;
      
      record_status(); // This takes a long time
    
      return NULL;
    }
    
    
    
    int
    main(void)
    {
      float       rate, sales, tax;
      pthread_t   helper;
      void        *result;
    
      pthread_create(&helper, NULL, thread_entry, NULL);
      
      rate = 0.05;
      sales = total_sales();
      
      if (should) tax = sales * rate;
      else        tax = 0.0;
      
      printf("Tax: %5.2f\n", tax);
      pthread_join(helper, &result);
      exit(0);
    }
    

    Specifically, the main thread may use the shared variable named should before the helper thread assigns a value to it.

    1. Eliminate the race condition by using a pthread_mutex_t variable and polling.
    2. Now, eliminate the need for polling by using a pthread_cond_t variable.

Copyright 2017