CSE 120: Homework #1

Spring 2025

Due: Tuesday, April 15 at 11:59pm

Each question is worth an equal number of points. When a question asks you to describe or explain, your answers can be relatively brief. The [name] tags give attribution to authors of other OS textbooks who wrote the question (you do not need the textbook to be able to answer the question, though). Please submit your answers as a PDF file to Gradescope.

  1. How do hardware and the OS work together to handle interrupts? When an interrupt happens, what tasks are handled by the hardware and what tasks are handled by the OS?

  2. [Anderson 2.1] For each of the three mechanisms that supports dual-mode operation — privileged instructions, memory protection, and timer interrupts — explain what might go wrong without that mechanism, assuming the system only had the other two. (In other words, if we just had memory protection and timer interrupts but not privileged instructions, what could go wrong; if we just had privileged instructions and timer interrupts, what could go wrong, etc.)

  3. [Anderson 2.13] Suppose you have to implement an operating system on hardware that supports interrupts and exceptions but does not have an explicit syscall instruction. Can you devise a satisfactory substitute for syscalls using interrupts and/or exceptions? If so, explain how. If not, explain why. (In this context, the syscall instruction is the instruction used by a user-level process to invoke a system call in the operating system.)

  4. [Silberschatz] Which of the following instructions should be privileged? Give a one-sentence explanation for why.

    a) Set value of timer
    b) Read the clock
    c) Clear memory
    d) Turn off interrupts
    e) Switch from user to kernel mode

  5. An important feature of the process abstraction is that the operating system has complete control over when a process executes, and the OS can arbitrarily suspend or resume a process at its discretion. As a result, applications are generally implemented to be independent of the passage of real time. However, many applications want to know about the passage of real time to implement various features, even as simple as a cursor that blinks every second. To support such functionality, OSes provide an interface to inform applications of the passage of real time. OSes typically use the same mechanism as with exceptions (e.g., divide-by-zero) to interrupt and notify a process as time passes.

    Below is a small C program that prints the passage of time in seconds using the simple Unix alarm interface (see man 2 alarm on ieng6):

    #include <unistd.h>
    #include <signal.h>
    #include <stdio.h>
    
    volatile int seconds;
    
    void handler (int signum) {
        seconds++;
        alarm (1);   // schedule next SIGALRM
    }
    
    int main (int argc, char *argv[]) {
        int last = seconds = 0;
        signal (SIGALRM, handler);  // SIGALRM is the timer signal
        alarm (1);   // schedule SIGALRM handler callback, arg is # of seconds
    
        while (1) {  // normally we would do useful work instead of spinning
          if (last != seconds) {
              printf ("%d sec\n", seconds);
              last = seconds;
          }
          if (last == 5) return 0;
        }
    }
    

    a) What is the output of this program? To verify your answer, you can compile and execute the program on ieng6:

    $ cc file.c          [assuming you put the program contents into file.c]
    $ ./a.out
    

    b) Generally it is not possible to predict how long a program will take to execute by inspecting its code. However, for this program we can. Approximately how long does it take to execute? (You can run it on ieng6 with time ./a.out.)

  6. [GNU C Library manual] The use of mechanisms like Unix signals introduces a source of non-determinism and concurrency in the execution of a process, even for single-threaded applications. Consider the following C program:
    #include <unistd.h>
    #include <signal.h>
    #include <stdio.h>
    
    volatile struct data { long int one, two; } value;
    
    void handler (int signum) {
        if (value.one != value.two)
          printf ("%ld != %ld\n", value.one, value.two);
        alarm (1);   // schedule next SIGALRM
    }
    
    int main (int argc, char *argv[]) {
        static struct data zeros = { 0, 0 }, ones = { 1, 1 };
        signal (SIGALRM, handler);  // SIGALRM is the timer signal
        alarm (1);   // schedule SIGALRM handler callback, arg is # of seconds
    
        while (1) {
          value = ones;
          value = zeros;
        }
    }
    

    The program is a tight loop assigning ones and zeroes to value. Is it possible for the program to ever print out the != message in handler? Briefly explain why or why not. (Again you can compile the program and experiment with it.)

  7. [Tanenbaum] For each of the following Unix system calls, give a condition that causes it to fail: open, read, fork, exec, unlink (delete a file). (Hint: We discussed some in lecture and you can also explore the error semantics of these system calls using man on ieng6, e.g., man 2 fork.)

  8. Consider the following C program:
    #include <stdlib.h>
    
    int main (int argc, char *arg[])
    {
        fork ();
        if (fork ()) {
    	fork ();
        } else {
    	char *argv[2] = {"/bin/ls", NULL};
    	execv (argv[0], argv);
            fork ();
        }
    }
    

    a. How many total processes are created (including the first process running the program)? (Note that execv is just one of multiple ways of invoking exec, see man 3 exec for all possibilities.) You do not need to provide an explanation.

    b. How many times does the /bin/ls program execute?

    [Hint: You can always add debugging code, compile it, and run the program to experiment with what happens.]