Lecture 4: Threads and Scheduling

Threads

Multiple processes allow programmers to "do multiple things at the same time", but communication between processes is difficult. IPC is possible but invokes system calls and may require copying large amounts of data between processes

Threads are like processes, but they share an address space. Multiple threads within a single process can communicate by simply reading and writing to shared variables in memory.

To create a thread, a programmer simply makes a system call to the kernel to fork a new thread (similar to how one forks a new process). The kernel creates a new Thread Control Block (TCB) to store the state of each thread. The thread control block shares a PCB with the parent thread.

The state of the computation (registers, ready/runing/waiting) is stored in the TCB, while the shared process-level information (VM configuration, permissions) are stored in the shared PCB.

This design is referred to as kernel level threading (or simply "kernel threads"), because the kernel is responsible for managing the TCBs. An alternative design is user-level threading, in which processes manage their own threading, and switch between threads using normal jump instructions inside an application-level scheduler. The Async library used in recent offerings of CS3110 is an example of a user-level threading library.

In order to support user-level threading, the kernel must provide a way for applications to request I/O without being transitioned to the waiting state. This is referred to as non-blocking or asynchronous I/O.

Scheduling

Thus far when discussing time-sharing between processes, we've simply said that when it is time to switch processes, the operating system selects a new process and then runs it. The details of "when it is time" and which process to select can have major impacts on system behavior.

We would like a scheduler that satisfies the following criteria:

We discussed the following algorithms: - First-come, first-served (FCFS): whenever a process becomes ready, it is placed at the tail of a queue. Whenever a process relinquishes the CPU, a new process is taken from the head of the queue and scheduled. - Pros: simple, fair (no process starves). - Cons: I/O bound and CPU/bound processes are treated the same, so waiting time, responsiveness, priority and predictability can be poor.

None of these algorithms are perfect. In reality, schedulers are hand-tweaked over time to work well in practice based on known and experimental workloads.