Windows Programming/User Mode vs Kernel Mode
In Windows (and most modern operating systems), there is a distinction between code that is running in "user mode", and code that is running in "kernel mode". This chapter is going to point out some of the differences. Firstly, Intel CPUs have modes of operation called rings which specify the type of instructions and memory available to the running code. There are four rings:
- Ring 0 (also known as kernel mode) has full access to every resource. It is the mode in which the Windows kernel runs.
- Rings 1 and 2 can be customized with levels of access but are generally unused unless there are virtual machines running.
- Ring 3 (also known as user mode) has restricted access to resources.
The reason for this is because if all programs ran in kernel mode, they would be able to overwrite each others' memory and possibly bring down the entire system when they crashed.
When a program is started (e.g. a web browser or a word processor), it runs in its own process. A process contains its own "virtual" memory space and resources. Its memory is "virtual" because the process thinks memory that is at address
0x12345678 may actually be at address
0x65f7a678 in physical memory. Similarly, two different processes may have different data stored at (to them)
0x00401000. This is implemented by dividing memory into chunks called pages; on x86 systems one page is 4 kilobytes in size. Each page can have its own set of attributes, such as read-only/read-write. The CPU has a transparent mechanism for translating virtual addresses to physical addresses through a page table which the operating system sets up.
Virtual memory is useful for many reasons:
- The process cannot access other process' memory,
- Each page can have different protection settings (read-only or read-write, kernel-mode-only), and
- Inactive memory regions of the process can be "paged out" (stored) to the pagefile and be retrieved by the operating system when needed. This is also done when the system is low on physical memory.
Every process started by Windows (with the exception of the System "process") runs in user mode. In this mode, programs cannot modify paging directly and so have no way of accessing other programs' memory except through API functions. Programs in user mode also cannot interfere with interrupts and context switching.
Kernel Mode, Interrupts, and System Calls
When Windows is first loaded, the Windows kernel is started. It runs in kernel mode and sets up paging and virtual memory. It then creates some system processes and allows them to run in user mode. How does the CPU ever switch back to kernel mode then? This is not done automatically by the CPU. The CPU is often interrupted by certain events (timers, keyboard, hard disk I/O), and these are called interrupts. The kernel must first set up interrupt handlers to deal with these events. Then, whenever interrupts occur, the CPU stops executing the currently running program, immediately switches to kernel mode, and executes the interrupt handler for that event. The handler saves the state of the CPU, performs some processing relevant to that event, and restores the state of the CPU (possibly switching back to user mode) so the CPU can resume execution of the program.
When a program wants to call a Windows API function1, it triggers an interrupt2 which causes the CPU to switch to kernel mode and begin executing the desired API function. When the API function has finished processing, it switches back to user mode and resumes execution of the program. This is because API functions like
ReadProcessMemory cannot work in user mode; the program can't access other programs' memory. In kernel mode, however, the API function can read any memory region without restriction.
1. Actually, Windows API functions eventually call a different API: the Native API. This is the API used by the Windows NT family of kernels. This is when the CPU switches to kernel-mode.
2. Modern CPUs have special, faster instructions for system calls, such as
sysexit on x86. These instructions cause the CPU to switch to ring 0, and then begin executing a handler set up by the operating system.
So, a program runs and calls API functions. How do other programs get a chance to run, then? Most of the time, programs simply allow the operating system to switch to another program because they are waiting for something (human input, hard disk). These programs are known as unrunnable programs, and since they make calls to the kernel to wait for something, the kernel knows to perform context switching to allow another program to run. This is done by:
- Saving the current program's state (including registers),
- Figuring out which program to run next,
- and restoring a different program's state.
If a program (thread or process to be more accurate) runs for more than a certain period of time (the thread quantum or a process's time slice), the operating system will context switch to another program. This idea is called preemption. Preemption is accomplished by setting a timed interrupt in the processor that will invoke context switching. The time slice that is used may be different for each process.