Page 123 - DCAP103_Principle of operating system
P. 123
Principles of Operating Systems
Notes 4.4.2 Memory Protection
Fault tolerance begins with memory protection. For many years, microprocessors have included
on-chip memory management units (MMU) that enable individual threads of software to run
in hardware-protected address spaces. But many commercial real-time operating systems never
enable the MMU, even if such hardware is present in the system.
When all of an application’s threads share the same memory space, any thread could-intentionally
or unintentionally-corrupt the code, data, or stack of another thread. A misbehaved thread could
even corrupt the kernel’s own code or internal data structures. It is easy to see how a single
errant pointer in one thread could easily bring down the entire system, or at least cause it to
behave unexpectedly.
For safety and reliability, a process-based real-time operating system (RTOS) is preferable. To
create processes with individual address spaces, the RTOS need only create some RAM-based
data structures and enable the MMU to enforce the protections described therein. The basic idea
is that a new set of logical addresses is “switched in” at each context switch. The MMU maps a
logical address used during an instruction fetch or a data read or write to a physical address in
memory through the current mapping. It also flags attempts to access illegal logical addresses,
which have not been “mapped” to any physical address.
The cost of processes is the overhead inherent in memory access through a look-up table. But
the payoff is huge. Careless or malicious corruption across process boundaries is rendered
impossible. A bug in a user interface thread cannot corrupt the code or data of a more critical
thread. It’s truly a wonder that non-memory protected operating systems are still used in complex
embedded systems where reliability, safety, or security are important.
Enabling the MMU has other benefits as well. One big advantage stems from the ability to
selectively map and unmap pages into a logical address space. Physical memory pages are
mapped into the logical space to hold the current process’ code; others are mapped for data.
Likewise, physical memory pages are mapped in to hold the stacks of threads that are part
of the process. An RTOS can easily provide the ability to leave a page’s worth of the logical
addresses after each thread’s stack unmapped. That way, if any thread overflows its assigned
stack, a hardware memory protection fault will occur. The kernel will suspend the thread
instead of allowing it to corrupt other important memory areas within the address space (like
another thread’s stack). This adds a level of protection between threads, even within the same
address space.
Memory protection, including this kind of stack overflow detection, is often helpful during
the development of an application. Programming errors will generate exceptions that are
immediately detected and easily traceable to the source code. Without memory protection, bugs
can cause subtle corruptions that are very difficult to track down. In fact, since RAM is often
located at physical address zero in a flat memory model, even NULL pointer dereferences will
go undetected! (Clearly, logical page zero is a good one to add to the “unmap list.”). Another
issue is that the kernel must protect itself against improper system calls.
The kernel must protect itself against improper system calls. Many kernels
return the actual pointer to a newly created kernel object, such as a semaphore,
to the thread that created it, as a handle. When that pointer is passed back
to the kernel in subsequent system calls, it may be dereferenced directly.
But what if the thread uses that pointer to modify the kernel object directly,
or simply overwrites its handle with a pointer to some other memory. The
results may be disastrous.
116 LOVELY PROFESSIONAL UNIVERSITY