One question that I bumped into when starting in OS development is where to put the kernel in memory. You have to think about where things such as applications and data will end up so that they don’t collide with your precious kernel. After all, the kernel will be present at all times and you don’t want it to be in the way so where do you place it?
Well, the simple solution is the one that GRUB gives you which is to load it at the 1MB mark. In fact, GRUB won’t let you load anything below 1MB, probably because that area is littered with reserved areas for things such as the screen memory, BIOS data and so on. Loading the kernel at 1MB is also quite safe because PC computers of today most likely have more than 1MB of memory. (I remember my 486 had 16MB RAM and that was back in 1994 so it should be safe!)
Virtual address space
But PC’s have something called virtual memory mapping, which means that you can map virtual addresses to physical addresses. Even if the kernel is loaded at 1MB, we can "pretend" that it is placed elsewhere using this memory mapping technique. This is where the question of "high or low" comes into the picture. We can map the kernel to address zero if we want. Or we can put it high up at the 2GB mark. This is possible even if the machine does not actually have 2GB of memory, simply because we map virtual address space to physical ones.
Which solution is the best? Well, let’s say we put the kernel at address zero. Where can we put the applications then? We need to reserve some space for kernel data and book keeping records so maybe we can place programs at 512MB? This means that the kernel gets the first 512MB of address space and applications get the rest.
This would be fine if applications did not use static linking. Let me explain…
Relocation vs static linking
Static linking means that all addresses used in an application are "hard coded" to a certain base address. The application MUST be loaded to this base address or it will crash. This does not sound very smart, but it’s really convenient when you pair it with virtual addressing. All applications can get the same address range (without colliding) so it’s really easy to load them to a certain base address.
The alternative is to use a relocation table in the executable which can be used to move it to any base address. However, this comes at a performance penalty as you have to go through all address pointers and relocate them to the new base address. This technique is commonly used for dynamic libraries that are loaded by applications and may collide with other libraries in memory.
I remember that relocation tables were used on the Amiga because it did not have any virtual memory mapping (at least not the first models). In order to load many programs at the same time they had to be relocatable. It’s not that relocation is evil, but I guess static linking is used extensively for performance reasons nowadays.
The problem…and solution
So back to my question – put kernel high or low?
Well, if our kernel sits at address zero and we compile and link programs so that they run from the 512MB mark in memory we may eventually get into trouble. Why? Because we have no space for expanding the kernel. If our kernel needs to grow in size to 1GB we are in deep trouble because then all applications have to be recompiled to a new base address!
Clearly this is bad…We can either require all apps to use relocation tables and pay the penalty, or we can choose another way.
The solution that both Windows and Linux employs is to put the kernel high up where it won’t interfere with the apps. Windows puts the kernel at 2GB (or 3GB with a special registry hack). This gives applications a nice 0-2GB address space to play with without having to touch the sensitive kernel private parts. It also makes it easy to switch to a 64 bit architecture without having to recompile all programs. On a 64 bit system the kernel is simply recompiled at a new location way way high up in memory, and applications still live at address zero but have lots more space to play with.
Placing the kernel high is the way to go in my opinion. Not all hobby OS devs agree on this, but that’s the way I will go. In my next post I will explain how it is done, quite easily.