Kernel Space and User Space
This article is based on the question I was asked in my recent interview for Embedded Software Engineer position. This article requires readers to know basic concepts like system call, Interrupt, Signals, Stack space etc,.
What is basic difference between Kernel mode and User mode?
Kernel Mode: In Kernel mode, the executing code has complete and unrestricted access to the underlying hardware. It can execute any CPU instruction and reference any valid memory address. Any operating system function below the system call layer, executes in kernel mode. Crashes in kernel mode are catastrophic; they will halt the entire PC because there is no other software layer to handle the crashes.
User Mode: In User mode, the executing code has no ability to directly access hardware or reference kernel memory. Code running in user mode must delegate to system APIs to access hardware or kernel memory. Most of the code running on your computer will execute in user mode.
What are the conditions under which a process switches from user space to the kernel space?
The transition from user space to the kernel space is usually caused by one of the following reasons:
- Software Interrupt: Fault/Exception (e.g. a page fault, divide-by-zero, floating-point exception or some other exception caused during the execution of an instruction)
- Hardware Interrupt: It is raised whenever a hardware needs CPU’s attention (e.g. a keyboard interrupt or I/O finishing).
- System calls (which includes accessing devices) also causes a context switch from userspace to kernel space.
- Signals usually initiated by a process(kill()) or the OS kernel (e.g: SIGFPE, SIGSEGV, SIGIO).They are managed by the OS kernel, which delivers them to the target thread/process, invoking either a generic action (ignore, terminate, terminate and dump core) or a process-provided signal handler.
- CPU instruction - Trap (e.g. a system call, breakpoint)
Normally, whenever any of the above condition occurs, the system checks the Interrupt Descriptor Table or Interrupt Vector Table. Each exception (interrupt, fault, etc.) has a number associated with it which is used to index into this table. From this table the CPU can determine which interrupt handler to run. This table contains an address for the Interrupt handler routine, which performs all the necessary steps required to switch the user application to kernel mode and start executing kernel instructions on behalf of user process.
How does this transition take place? What are the steps involved?
As part of the transition generally the following changes take effect:
- Switch to Kernel stack
- EFLAGS are saved
- Code segment selector and EIP are saved.
- Stack segment selector and stack pointer are saved
- Start executing the interrupt handler
- The general purpose registers are saved (handler's job)
- Segment selectors are changed to Kernel ones (handler's job)
Both Interrupt vector table (IVT) and Interrupt descriptor table (IDT) are just same. IVT is only valid in real-mode and IDT is valid in the protected mode. In real mode, the lower 1K of the main memory holds a data structure known as the Interrupt Vector Table (IVT).There are nominally 256 entries in this table. (Since the 80286, the IVT is not required to have 256 entries or start at physical address 0). . The Interrupt Descriptor Table Register (IDTR) holds the base address and the length of IVT/IDT. Each entry contains a far pointer to an Interrupt Service Routine. Any type of interrupt request routes to the appropriate Interrupt Service Routine through this table. The processor indexes the interrupt number in this table; pushes current CS, IP, and flags on the stack; and calls the far pointer specified in the IVT. The handler processes the interrupt and then executes an IRET instruction to return control to the place where the processor executed at the time of the interrupt.
Note: The IDT must exist in physical memory and should never swap out to virtual memory. This is because if an interrupt were to occur while the IDT were swapped out, the processor would generate an exception, requiring the IDT to get the handler for handling this exception, and so on until the system crashed.
One way a user space application can explicitly initiate a switch to kernel mode during normal operation is by making an system call such as open, read, write etc. Whenever a user application calls any of these system call APIs with appropriate parameters, a software interrupt/exception(SWI) is triggered. As a result of this SWI, the control of the code execution jumps from the user application to a predefined location in the Interrupt Vector(Descriptor) Table provided by the OS.
Let us illustrate this using an example, say a regular system call, like read. The function's role is copying data from a source, (usually a device, either a mass-storage or a communication medium) to buffers held in the application. The below diagram clearly demonstrate the whole process.