In the last OS article
, we had an introductory look at the kernel, considered what language to use to implement an OS and discussed some of the x86 architecture. In this, second installment, I am going to look deeper into the kernel and introduce the concept of memory management. This topic can get quite technical, I have tried to keep it simple but if you have any questions do not hesitate to ask!
Random Access Memory (RAM) is absolutely key - not only is program data stored inside memory, also the operating system will load up programs into memory and start their execution. However, the OS has a much wider job to do - whenever a program wishes to use memory it asks (directly or indirectly) the operating system to give it some free memory to use. It is therefore essential that the kernel can manage memory efficiently, in terms of time, memory usage and reliability. Miss management of memory can lead to numerous problems.Memory Management Unit (MMU)
When dealing with memory addressing, there is in fact two types of address - physical addresses and virtual addresses. A physical address is the direct memory address used to refer to a real location in RAM. There are a number of downsides with just using physical addresses, firstly the program must be position independent (not rely on being in a specific area in memory) and secondly if some code becomes errornous, there is nothing to stop it overwriting other programs and their data. These problems have meant that physical addressing is obsolete in desktop operating systems, examples of OSes which use it are MS DOS and Amiga OS. The advantage of physical addressing is that its efficient, and as such is still sometimes used in embedded systems.
Virtual addressing solves many of our problems - virtual addresses can be mapped to any physical address. This provides a number of advantages - for each program a separate virtual address space can be created, which not only allows each program to start at (virtual) memory location 0 (so it always knows where it starts), it also means that programs can ONLY see their memory - so that an one program can not affect another.
So whats the MMU do? Well, basically, the MMU, part of the machine architecture, (under direction from the kernel) allows for the conversion between virtual and physical addresses (called mapping.) A program running on a system need not worry about whether the memory addresses are virtual or physical - this is all abstracted away. Converting addresses
It is very important that physical and virtual addresses map effectively. With the x86 architecture, there are two main ways that this is achieved - via segmentation or paging. With the following descriptions, I am only going to explain about protected mode operation (which is the most relevant, see article 1 for an explanation of protected mode.)Segmentation
On x86, we do not have a flat memory model (where each memory location continues from the previous one.) Instead, memory is split up into a number of different segments (or sections), each segment containing memory locations. Therefore, in order to access a specific area in logical memory you need to use an address such as Segment:Offset (where Segment is the segment number, Offset is the location in that segment from 0 to segment length.) Offset is a 32 bit number, which means that each segment can contain up to 4GB of memory.
If you remember back to article 1, I briefly mentioned the Global Descriptor Table (GDT) - this table describes all the memory segments, and for each segment it contains information such as its length, type (code, data etc..), privilege level (for security), and the start address of that segment in physical memory. Therefore, when we access offset n of segment q, the MMU will look in the GDT, for segment q's information and will map offset n to its physical address memory (by adding address n to q's start address.)
To give an example, the operating system loads up a program. It knows that the program requires 1 KB of memory. At this point it will add into the GDT a segment entry (lets call this segment 9), of length 1KB, and will start this segment at physical memory location 15424. The program itself will just see 1KB of memory, and access this via virtual address 0 - 1023 (for each byte in memory.) So, lets say the program access virtual addresses 512 - the kernel will pass this onto the MMU with the segment number (9:512) - the MMU will look up segment 9 in the GDT, find that it starts at physical memory location 15424 and add 512 to this (to access physical memory location 15936.)Paging
Here the system breaks memory up into groups of pages. In essence, its quite simple, there is a paging DIRECTORY - which, for each entry (and there can be up to 1024), points to a specific paging TABLE. In a paging table, each entry (there can be a further 1024) points to a physical memory address. To map to a virtual address, we take account of both the location in the paging table and in the paging directory.
As an example, lets say that our OS loads a program up, requiring 1KB memory, and creates a paging table mapping each entry to physical addresses 10000 to 101023. An entry is then created in a paging directory (for instance lets say its entry 12) to point to this table. When accessing the virtual addresses (0 to 1023 for each byte) the program will pass this to the kernel, which will then find the paging directory and the table's entry in that directory. Lets say the program is accessing location 512 again - the kernel will pass to the MMU entry 12 in the directory, location 512 in the paging table. The MMU will then go into the paging directory, find paging table 12 and access location 512 to get the physical address 10512.
Image used courtesy of osdev.org
Using paging, different processes can take very different views of the same memory as can be seen in the above illustration. Depending entirely on the architecture and OS, each process can have its own paging table and/or paging directory,Virtual Memory
One extra problem - we often require more memory than we actually have, especially with Vista! When physical memory (RAM) is exhausted, free space on the hard drive can be used to provide a form of "virtual" RAM. Using the paging approach (most popular in terms of virtual memory), when RAM is exhausted, pages which are not being currently being accessed are removed from physical memory and written to the hard drive (called a swapfile on Linux.) - this is called "paging out".
There are two problems with this - firstly it requires lots of extra work by the kernel to keep track of where pages are, and secondly (and more importantly) its very inefficient when a process requires a page which is swapped out and must be retrieved from the hard drive. Finding an efficient way of providing this mechanism is not only very important, but also challenging! This swapping system assumes that a process doesn't need all its memory all of the time - its known as "working set" abstraction, in order to run correctly a process requires at least its working set of pages. Get this wrong, and there is a risk of "thrashing" - which is where a process constantly requires pages to be swapped in, but because of memory limitations other pages in the working set need to be swapped out whilst they are still needed.
Virtual memory provides for a nice view - the CPU cache and RAM can be both thought of as a cache above the hard drive, which provides the real memory limitation. Although page swapping is the most common form of virtual memory implementation, it can also be implemented with segment swapping. The kernel may even do process swapping - which swaps out entire idle process's memory.Further Information
As ever, there is much further information available on the topic of memory management for those interested. A good tutorial on this can be found at http://www.osdever.net/tutorials.php?cat=6&sort=1
The next part of this article is OS Development 3 - Processes and Threads
Article Source: http://www.TechnicalTalk.net
*** Please do not forget to leave your comments about this article ***