The Software · Chapter 9.a
How does a program ask for help?
Transcript
How does a program ask for help? It can't reach the disk on its own, yet it saves files all day.
I figured a program just writes to the disk directly, like writing to its own memory. But then one buggy program could wreck every other one.
So there's a wall. Up top, user mode: the program. Below, kernel mode: the privileged core that owns the disk and the screen. The only door is a trap, called a system call. The program sets a request and a value, like write forty-two, and traps. The kernel looks up routine zero, writes the value, and hands back a result. Control returns to the program, right after the trap.
So a program asks for help by knocking on the only door! The trap is the only door from a program into the kernel.
Set two numbers below, and watch every request cross the wall: five of them.
- The browser
- The network
- Software and languages
- The CPU
- Memory
- Arithmetic
- Logic gates
- The switch
The operating system of Chapter 9 shares the processor among programs, but sharing is only half its job. The other half is protection, enforced by the privileged core of the operating system, the kernel. A program runs in a restricted mode where it cannot reach the disk, the network, or another program’s memory on its own. If it could, one buggy or hostile program could wreck every other. In a real processor a hardware mode bit enforces this boundary; here it is modeled by discipline, the program holding a reference to the kernel object but meant to cross only through the syscall, not by anything the machine itself prevents. So when a program needs something only the system can safely do, it does not do it. It asks.
Asking means executing a trap: the program leaves a request number and an argument where the kernel will look, then runs a special instruction that hands control to the kernel in its privileged mode. The kernel checks the number against its menu of services, runs the matching routine, and returns. That trap is the only door from a program into the kernel.
Here a Python method call stands for the trap. This service model does not add a trap instruction or privilege mode to Chapter 5’s CPU, and it runs independently of the scheduler.
9.a.1 The trap: the only door
Figure 9.a.2 draws the two modes. Above the line, the program; below it, the kernel, holding the resources. The program cannot cross on its own. The trap carries it down, the kernel does the work, and a result comes back up.
syscall traps down into the kernel,
which does the privileged work and returns a result. The trap is the only door.Example 9.a-1. The kernel, serving one call
class Kernel: """The privileged side of the boundary: it holds the resources and the menu of routines that touch them, and serves one request at a time.""" def __init__(self, inputs: list, pid: int) -> None: self._inputs = list(inputs) # the queue a READ draws from self._pid = pid self.output = [] # everything a WRITE produced self.trace = [] # one record per call, for the transcript self._handlers = {WRITE: self._write, READ: self._read, GETPID: self._getpid} def _write(self, arg: int) -> int: self.output.append(arg) return 0 def _read(self, arg: int) -> int: return self._inputs.pop(0) def _getpid(self, arg: int) -> int: return self._pid def syscall(self, number: int, arg: int = 0) -> int: """The trap. A program crosses here and nowhere else. Reject any number not on the menu, otherwise run its routine and return the result.""" if number not in self._handlers:1 raise ValueError(f"unknown system call: {number}") result = self._handlers[number](arg)2 self.trace.append((number, arg, result)) return result
- the check at the door: a request number that is not on the menu is refused before anything runs, so a program cannot invent a service
- otherwise the matching routine runs, in the kernel’s privileged mode, and its result is what the program gets back
9.a.2 The menu of services
The kernel offers a small, fixed menu, each service a number the program names. Here it is three: write a value out, read one in, and report the program’s own id. The program strings these together to get its work done, never touching the resources behind them. Figure 9.a.3 shows a call being routed.
Example 9.a-2. A program that can only ask
def user_program(kernel: Kernel) -> None: """A program running in the restricted mode. It can read its inputs, learn its own id, and write results, but only by asking the kernel for each.""" pid = kernel.syscall(GETPID)1 kernel.syscall(WRITE, pid) # announce which process this is a = kernel.syscall(READ) b = kernel.syscall(READ) kernel.syscall(WRITE, a + b) # the answer it computed2
- the program learns even its own id by asking; it has no privileged state of its own
- and it reaches the screen only by handing the kernel a value to write, never by writing directly
9.a.3 The system calls, run
Running ftb.syscall runs the small program against a kernel and shows every trap it makes.
each system call: a trap into the kernel and back call arg returns GETPID 0 7 WRITE 7 0 READ 0 40 READ 0 2 WRITE 42 0 what the program got onto the screen: [7, 42] It reached the outside world only by asking; the kernel did the rest.
The program asked for its id, wrote it, read its two inputs, and wrote their sum. Five requests crossed the modeled boundary, and this cooperating program made no direct resource access. In a real system, hardware privilege and kernel checks enforce that boundary; the Python trace demonstrates dispatch, not protection against a caller that bypasses it.
The next chapter follows a memory request into a process’s heap allocator, then distinguishes its local bookkeeping from requests to the kernel for more memory.
Exercises
- Add a new system call that returns a fixed number, and call it from a program.
- Make a program ask for a system call that does not exist, and watch the kernel refuse it.
