Processes and Threads

Lecture Notes for CS 140
Winter 2013
John Ousterhout

  • Readings for this topic from Operating System Concepts: Sections 1.1-1.6, Section 3.1, and Section 3.3.

Processes

  • Process (slightly oversimplified): "An execution stream in the context of a particular process state."
    • Execution stream: a sequence of instructions (only one thing happens at a time).
    • Process state: everything that can affect, or be affected by, the process: code, registers, data, call stack, open files, network connections, etc.
  • Is a process the same as a program?
  • Uniprogramming system: only supports a single process at a time. Simplifies some parts of OS, but many other things are hard to do.
    • Some early personal computer operating systems used uniprogramming (e.g. MS-DOS), but these systems are almost unheard of today.
    • This is not the same as "uniprocessing": that refers to a system with only one processor.
  • Virtually all modern operating systems are multiprogramming systems: multiple processes can exist simultaneously and share the machine.

Threads

  • Most modern operating systems also support threads: multiple execution streams within a single process
    • Threads share process state such as memory, open files, etc.
    • Each thread has a separate stack for procedure calls (in shared memory)
    • Thread is unit of sequential execution
  • Why support threads?
    • Concurrency (large servers)
    • Structuring tool for programs (but mixed blessing)

Dispatching

  • OS uses a process control block to keep track of each process:
    • Execution state for each thread (saved registers, etc.)
    • Scheduling information
    • Information about memory used by this process
    • Information about open files
    • Accounting and other miscellaneous information
  • At any given time a thread is in one of 3 states:
    • Running
    • Blocked: waiting for some other event (disk I/O, incoming network packet, etc.)
    • Ready: waiting for CPU time
  • Dispatcher: inner-most portion of the OS that runs threads:
    • Run a thread for a while
    • Save its state
    • Load state of another thread
    • Run it ...
  • Context switch: changing the thread currently running on a CPU by first saving the state of the old process, then loading the state of the new process.
  • CPU can only be doing one thing at a time: if a thread is executing, dispatcher isn't: OS has lost control. How does OS regain control of processor?
  • Traps (events occurring in current thread that cause a change of control into the operating system):
    • System call.
    • Error (illegal instruction, addressing violation, etc.).
    • Page fault.
  • Interrupts (events occurring outside the current thread that cause a state switch into the operating system)):
    • Character typed at keyboard.
    • Completion of disk operation (controller is ready for more work).
    • Timer: to make sure OS eventually gets control.
  • How does dispatcher decide which process to run next?
    • Plan 0: search process table from front, run first ready thread.
    • Plan 1: link together the ready threads into a queue. Dispatcher grabs first thread from the queue. When threads become ready, insert at back of queue.
    • Plan 2: give each thread a priority, organize the queue according to priority. Or, perhaps have multiple queues, one for each priority class.
  • When thread isn't running, its state must be saved in process control block. What gets saved?

Process Creation

  • How the operating system creates a process:
    • Load code and data into memory.
    • Create and initialize process control block.
    • Create first thread with call stack.
    • Provide initial values for "saved state" for the thread
    • Make process known to dispatcher; dispatcher "resumes" to start of new program.
  • System calls for process creation in UNIX:
    • fork makes copy of current process, with one thread.
    • exec replaces memory with code and data from a given executable file. Doesn't return ("returns" into new process).
    • waitpid waits for a given process to exit.
    • Example:
      int pid = fork();
      if (pid == 0) {
          /* Child process  */
          exec("foo");
      } else {
          /* Parent process */
          waitpid(pid, &status, options);
      }
      
    • Advantage: can modify process state before calling exec (e.g. change environment, open files).
    • Disadvantage: wasted work (most of forked state gets thrown away).
  • System calls for process creation in Windows:
    • CreateProcess combines fork and exec
      BOOL CreateProcess(
          LPCTSTR lpApplicationName,
          LPTSTR lpCommandLine,
          LPSECURITY_ATTRIBUTES lpProcessAttributes,
          LPSECURITY_ATTRIBUTES lpThreadAttributes,
          BOOL bInheritHandles,
          DWORD dwCreationFlags,
          PVOID lpEnvironment,
          LPCTSTR lpCurrentDirectory,
          LPSTARTUPINFO lpStartupInfo,
          LPPROCESS_INFORMATION lpProcessInformation
      );
      
    • Must pass arguments for any state changes between parent and child.
  • Process creation in Pintos: exec combines UNIX fork and exec.