CS161 Day 3 In-Class Exercise

Defining Threads and Processes

Question 1: Where does OS/161 define the structures that represent a process and a thread?

Question 2: In OS/161's definition for a thread, you'll find this field:
    struct cpu *t_cpu; /* CPU thread runs on */
Why isn't this field in the definition for a process? In other words, why isn't a process associated with a CPU?

Copying Data Between the Kernel and the User-level

Consider the Linux implementation of the gethostname(char *name, int len) system call:
    struct rw_semaphore{
        long count;
        spinlock_t wait_lock;
        struct list_head wait_list;
    };
    struct rw_semaphore uts_sem;
    
    int gethostname(char *name, int len)
    {
        int i, errno;
        struct new_utsname *u;
        if (len < 0)
                return -EINVAL;
------->down_read(&uts_sem);
------->u = utsname(); //Call a kernel function to
                       //get the hostname.
        i = 1 + strlen(u->nodename);
        if (i > len)
                i = len;
        errno = 0;
        if (copy_to_user(name, u->nodename, i))
                errno = -EFAULT;
        up_read(&uts_sem);
        return errno;
    }
A corresponding system call named sethostname(const char *name, size_t len) allows the hostname to be written.

Question 3: If the order of the two arrowed lines were swapped, what problems might arise?

Question 4: Suppose that interrupts are not disabled when the kernel invokes sethostname(). What ramifications does this have for the ways in which interrupt handlers can read and write to hostname-related kernel data? Does it matter whether a piece of hostname-related kernel data is greater than the size of a word (e.g., 32 bits on a 32-bit machine)?

Question 5: In Linux, the operating system is mapped into the top of each process’s virtual address space:

The Linux copy_to_user() function copies kernel data to a user-supplied buffer. Give two concrete examples of how a malicious or buggy program can pass improper data to gethostname(char *name, int len), and describe how copy_to_user() would detect those errors and prevent the kernel from possible corruption.

File Descriptors

Question 6: What is the difference between the FILE * returned by fopen(), and the int fd returned by open()? When would one be more useful than the other to an application? To help answer those questions, draw an architectural diagram which shows the relationship between application code, libc, and the operating system.

Question 7: After a fork(), both the parent and the child should possess file descriptors with the same numbers and which point to the same files. However, imagine that the child and the parent write to the same location in the same file at the same time--which write should “win”? What if one process reads from the same offset in a file that the other process is writing--what data should the reading process read, and what data should the writing process write (if any)? Devise a high-level conflict management policy that the kernel can use to provide “reasonable” results in both scenarios, for some definition of “reasonable.” Then describe at a high-level how the kernel could implement such a policy.