Contents

Notes on Processes, Kernel/User Separation, Syscalls

Including questions from #readings as of late afternoon.

What is a process?

A process is a running instance of a program. It refers to the combination of the code for that program plus all of its run-time context:

  • Register values (general purpose registers, stack pointer, flags, program counter/instruction pointer)
  • Memory contents (stack, heap, globals, code, …)
  • State of OS resources the process is using, such as open files.

Notice that the program code itself is part of the process context, since it just more data in memory during program execution.

Since the process must share the hardware, this context information cannot always live in its natural place (especially the registers!).

The OS maintains its own internal process/task list data structure (also in memory) containing a process control block (a.k.a. process descriptor) for each process that holds all data and metadata the OS needs to manage that process. This includes:

  • Storage for all of the process context (or references to where it is stored).
    • Space to save register values (used only while the process is not running).
    • References to the process’s memory sections (more later).
  • Metadata about other resources (besides registers and memory) that are in use by the process, such as open files.
  • Metadata about the execution state of the process (running, ready, blocked).

So what is a process? Logically, it is a running instance of a program. Concretely, this includes all of this context, both the actual context data being manipulated directly by the running program, and the OS kernel metadata about the running program and its context data.

Is the kernel a process?

The short answer is no. The kernel is not a process. The kernel is OS code that runs on the CPU at a higher privilege level (kernel mode) using its own data structures in a separate part of memory. Kernel space usually refers to the kernel memory and also the kernel code that can manipulate it. In contrast, processes run in their own user space. The kernel has some context (memory contents), interacts with all processes, and in some sense runs hidden “inside” all processes, but is not its own process.

Aside

A longer answer is that, while the kernel is not a process, an OS may consist of a kernel and several supporting processes that assist the OS.

  • Some OSes, like Linux, use a monolithic kernel model, meaning roughly that all parts of the OS—from process control to file systems to networking to other I/O support—are just more functions inside the kernel that use data structures in kernel memory. To interact with the OS, user programs use system calls via traps.
  • Other OSes, like the macOS/iOS, and its predecessor the Mach kernel, use a microkernel model, meaning only the most minimal essential are part of the kernel and additional services like networking, file systems, I/O support and device drivers, all run as separate processes. To interact with these non-kernel services, user programs would use the same basic facilities they use to communicate with any other process on the system, client-server style, rather “calling” into kernel code via traps/systems calls.

Do user programs call kernel functions? What’s the difference between a system call and a function call?

User code does not use the call instruction to call kernel functions. In order to cross the kernel/user privilege boundary, a trap is used. Without the privilege barrier, user code could do whatever it pleases with the privileged kernel code whenever it wants, even call parts it’s not supposed to call. Where a normal function call is analogous to an undeniable demand, a system call provides is like a request. The trap escalates privileges to kernel mode and then runs the code that the kernel has set up to handle this kind of system call.

The kernel code to handle a given syscall does several things, including:

  1. Save the process’s register values (including stack pointer and program counter) into memory.
  2. Setup its own stack and register values.
  3. Check to see if the syscall request is well-formed, valid, and allowed.
  4. If the request is valid, then the kernel uses its privileges to complete the request. Otherwise prepare an error code as a result, or even terminate the process.
  5. Restore the process’s register values.
  6. Use a privileged “trap return” instruction to lower the privilege level and set the program counter register back to the instruction in the process’s code immediately following the trap instruction that started all this.

(Skipping some detail for now.) Steps 3-4 are critical for protection! Do not trust processes to make valid requests!

Questions posted on #readings stream

Great questions showing up! Keep them coming. I will usually let the discussion play out on #readings and then make sure we visit what looks important during class, then follow up about anything we missed later on #readings.

Process Structure: What parts are “in” the process vs in the OS?

Hopefully answered above.

Direction Execution: In general, how do traps/syscalls work?

See:

  1. Summary above in the difference between system calls and function calls
  2. The Syscalls diagrams/demo.
  3. More detail in an obscure CS 240 topic that we never give much attention in 240.

Why don’t we write the syscall asm directly? Where does it actually happen?

The explict trap instructions are all wrapped up inside function of the standard library. For example, if we disassemble the compiled code for open (a C standard library function to open a file given a path and a mode), we will eventually find:

movq $2, %rax
int $0x80

See the Syscalls diagrams/demo.

How does the kernel prevent the user from just syscalling maliciously?

The kernel establishes what code will run in response to a trap. This must be careful code that checks all the syscall arguments for validity before trying anything. See the Syscalls diagrams/demo.

How do timer interrupts work?

There’s actual hardware (not software) that can handle the timers. After the clock cycle count reaches N, this hardware delivers a trap to the CPU, which is handled via the same hardware mechanism as explicit trap instructions. This same mechanism is how physical network devices, user input devices, storage devices, anything else “external” to the CPU/memory can demand that the OS do something in response to an external event.

How does the OS mitigate cost of checking more frequently with non-cooperative?

There are interesting tradeoffs here! The timer interrupt mechanism makes it very cheap to “set an alarm for later.” But of course context-switching frequently between processes has cost. We will consider it more when we explore CPU scheduling in more depth next week.