Mechanism of Process Execution

Mechanism of Process Execution

This lecture covers how the OS runs processes, handles system calls, and performs context switches. It details privilege levels, kernel vs user stacks, trap instructions, interrupt descriptor tables, and saving/restoring context during process switches.

    Kernel Mode Execution

    Kernel Mode Execution

    This lecture explains the distinction between user mode and kernel mode execution in OS. It details how system calls and interrupts trap the CPU to kernel mode, context saving on kernel stack, interrupt descriptor table (IDT), and context switching in kernel mode.

      Processes

      Processes

      This lecture introduces processes as running instances of programs. It covers process life cycle management, process states, process control blocks, and system calls like fork, exec, exit, and wait. It explains process scheduling, context switching, and how the shell executes commands.

      What a process actually is

      A program is a passive thing: a file on disk containing instructions and initial data. A process is that program in motion, and it carries state the file never had. Two users running the same editor binary produce two processes with separate memory, separate open files and separate positions in the instruction stream.

      The abstraction exists so that a limited number of CPUs can be shared by many programs. The operating system gives each process the illusion of owning a CPU and a private address space, and multiplexes the real hardware underneath. Getting that illusion right is most of what the process subsystem does.

      A process’s machine state has three parts you should be able to name in an exam:

      • Address space. The memory the process can legally touch: code, static data, heap and stack.
      • Registers. The general-purpose registers, plus the program counter, which holds the address of the next instruction, and the stack pointer.
      • I/O state. The files and devices the process currently has open.

      Process states

      A process moves through a small set of states. The three that matter are:

      • Running. The process is executing on a CPU right now.
      • Ready. The process could run, but the scheduler has given the CPU to someone else.
      • Blocked. The process cannot proceed until some event completes, usually I/O. A blocked process is not a candidate for scheduling.

      Two more appear in most textbook diagrams: new, while the process is being created and admitted, and terminated (often called the zombie state on Unix), where the process has finished but its exit status has not yet been collected by its parent.

      The transitions are worth learning as a set. Ready to running is a schedule; running to ready is a deschedule, which happens on a timer interrupt or when a higher-priority process becomes ready. Running to blocked happens when the process issues a blocking I/O request. Blocked to ready happens when that I/O completes. Note that a blocked process never goes directly back to running; it must pass through ready and be chosen by the scheduler.

      The process control block

      The operating system tracks each process with a data structure usually called the process control block, or PCB. On Linux the equivalent structure is task_struct. It holds everything the kernel needs to suspend a process and resume it later as if nothing happened:

      • Process identifier (PID) and the parent’s PID
      • Current state
      • Saved register context, including program counter and stack pointer
      • Memory management information: page tables or segment base and bounds
      • Open file descriptor table
      • Scheduling information such as priority and accumulated CPU time
      • Accounting and signal-handling information

      The saved register context is the critical field. It is what makes a process resumable, and it is what gets written and restored during a context switch.

      Creating and controlling processes

      Unix separates process creation from program loading, which surprises people coming from other systems but turns out to be a clean design.

      fork

      fork() creates a near-identical copy of the calling process. The child gets its own address space, initially a copy of the parent’s, and its own PID. The call returns twice: in the parent it returns the child’s PID, and in the child it returns 0. That difference in return value is the only thing distinguishing the two paths.

      pid_t rc = fork();
      if (rc < 0) {
          // fork failed
      } else if (rc == 0) {
          // child executes here
      } else {
          // parent executes here, rc holds the child's PID
      }

      The order in which parent and child run after the fork is decided by the scheduler and is not guaranteed. Code that depends on one running first is incorrect.

      exec

      exec() replaces the current process image with a new program. It does not create a process. The PID stays the same; the code, static data, heap and stack are all overwritten by the new executable, and execution begins at its entry point. A successful exec never returns, because there is no longer any code to return to.

      wait and exit

      exit() terminates the calling process and hands an exit status to the kernel. wait() lets a parent block until a child terminates and collect that status. Until the parent calls wait, the terminated child remains as a zombie: its PCB is retained purely to hold the exit status. A parent that never waits leaves zombies accumulating in the process table.

      The reverse case is an orphan, a child whose parent exited first. Orphans are re-parented to the init process, which waits on them routinely, so they are cleaned up.

      Context switching

      A context switch is the mechanism by which the operating system stops one process and starts another. The steps are:

      1. A trap or interrupt transfers control from the running process to the kernel.
      2. The kernel saves the current process’s register state into its PCB.
      3. The scheduler selects the next process to run.
      4. The kernel restores that process’s register state from its PCB, including its program counter.
      5. Control returns to user mode, and execution resumes exactly where that process left off.

      The switch is not free. Beyond the direct cost of saving and restoring registers, there is an indirect cost: the new process finds the CPU caches and the TLB filled with the previous process’s data, and runs slowly until they warm up. This indirect cost is often larger than the direct one, and it is the reason very small scheduling quanta hurt throughput.

      How the shell runs a command

      The fork-then-exec split exists to give the shell a window in which to act. When you type a command, the shell:

      1. Calls fork() to create a child.
      2. In the child, adjusts the environment before loading the new program. This is where redirection happens: closing standard output and opening a file in its place means the new program writes to the file without knowing anything about it.
      3. Calls exec() in the child to load the requested program.
      4. Calls wait() in the parent, so the shell blocks until the command finishes and only then prints the next prompt.

      Redirection and pipes both work because the descriptor manipulation happens between the fork and the exec. A combined “create process and run program” call would leave nowhere to do it.

      Quick revision summary

      • A program is a file; a process is a program executing, with its own address space, registers and I/O state.
      • Core states are running, ready and blocked. Blocked never returns directly to running.
      • The PCB stores everything needed to resume a process, above all the saved register context.
      • fork creates a process and returns twice. exec replaces the program and never returns. wait collects a child’s exit status.
      • Zombie: terminated but not yet waited on. Orphan: parent died first, re-parented to init.
      • Context switch cost is direct (saving registers) plus indirect (cold caches and TLB), and the indirect part usually dominates.
      Device Driver and Block I/O in xv6

      Device Driver and Block I/O in xv6

      Covers file system layers: device driver communication with disk controller, buffer cache management, block read/write operations, and logging for crash consistency.