1

I'm having an incredibly tough time making sense of this excerpt from the Linux device drivers book (sorry for text-heavy post):

The kernel (on the x86 architecture, in the default configuration) splits the 4-GB virtual address space between user-space and the kernel; the same set of mappings is used in both contexts. A typical split dedicates 3 GB to user space, and 1 GB for kernelspace.

Ok, got it.

The kernel’s code and data structures must fit into that space, but the biggest consumer of kernel address space is virtual mappings for physical memory.

What does this mean? Aren't the kernel's code and data structures also in "virtual memory that's mapped to physical address space." Otherwise where are these code and data structures even stored?

Or is this saying that the kernel needs virtual address space to map random non-kernel related data that it's operating on via drivers, IPC or whatever?

The kernel cannot directly manipulate memory that is not mapped into the kernel’s address space. The kernel, in other words, needs its own virtual address for any memory it must touch directly.

Is this even true? If the kernel is running in the context of a process (handling a syscall), the process' page tables will still be loaded, so why can't the kernel read usermode process memory directly?

Thus, for many years, the maximum amount of physical memory that could be handled by the kernel was the amount that could be mapped into the kernel’s portion of the virtual address space, minus the space needed for the kernel code itself.

Ok, if my understanding in quote #2 is correct, this makes sense.

As a result, x86-based Linux systems could work with a maximum of a little under 1 GB of physical memory.

???? This seems like a complete non sequitur. Why can't it work with 4GB of memory and just map different stuff into the 1GB space available for the kernel as needed? How does the kernel space only being ~1GB mean the system can't run with 4GB? It doesn't have to all be mapped at once.

2

Why can't it work with 4GB of memory and just map different stuff into the 1GB space available for the kernel as needed?

It can, that's what the HIGHMEM config options do for memory that doesn't fit to be mapped directly. But when you need to access an arbitrary location in memory, it's much easier to do that if you can point to it directly, without setting up a mapping every time. For that, you need an area of virtual memory that's always mapped to all of the physical memory, and that can't be done if the virtual address space is smaller than the physical.

Direct access is also faster, vm/highmem.txt in the kernel docs says:

The cost of creating temporary mappings can be quite high. The arch has to manipulate the kernel's page tables, the data TLB and/or the MMU's registers.

Sure, you can access the running process's memory through the user space mapping, and perhaps you can avoid the need to access the memory of other processes. But if there are any large in-kernel data structures (like the page cache), it would be nice to be able to use all the memory for them.

The whole thing is a sort of bank switching, which was something that was used in 16-bit machines, and in 386/486 systems in the DOS-era (HIMEM.SYS). I don't think anybody particularly liked accessing memory like that even then, since it makes things rather difficult if you need to have multiple areas of the physical memory "open" at the same time. Evolving to 32-bit and then to 64-bit systems has cleared that problem.

1

If the kernel is running in the context of a process (handling a syscall), the process' page tables will still be loaded, so why can't the kernel read usermode process memory directly?

The wording "the kernel's address space" should, in this context, not be interpreted as opposed to the user address space. Instead, what is meant is that the memory that the kernel needs to access must be mapped to some virtual addresses. This is the point the book author is trying to make here. Thus "the kernel's address space" is the whole mapping.

1

it can. "for many years" it didn't; originally there was no reason to do so because no-one had that much RAM.

you need to keep reading a bit further, look carefully.

The limitation on how much memory can be directly mapped with logical addresses remains, however. Only the lowest portion of memory (up to 1 or 2 GB, depending on the hardware and the kernel configuration) has logical addresses;[2] the rest (high memory) does not. Before accessing a specific high-memory page, the kernel must set up an explicit virtual mapping to make that page available in the kernel's address space. Thus, many kernel data structures must be placed in low memory; high memory tends to be reserved for user-space process pages.


If the kernel is running in the context of a process (handling a syscall), the process' page tables will still be loaded, so why can't the kernel read usermode process memory directly?

it does

https://www.quora.com/Linux-Kernel-How-does-copy_to_user-work


It may also be useful to understand that the usual use of kmalloc() to allocate structures in the kernel, returns memory which is within the ~1GB direct mapping. So that's lovely and straightforward to access.

(The tradeoff is that it introduces complexity in the form of these different types of allocations.

If you wanted standard kmalloc() allocations to be able to use more than 25% of RAM, you'd be doing something fairly demanding... In more specialized cases you can set the GFP_HIGHMEM flag and map & unmap the memory as needed. But the official answer is you're just not supposed to try and run such a demanding workload on a legacy 32-bit systems stuffed with over 30 bits worth of physical RAM).

If you're really interested in this specific detail, I noticed two other things.

1. The 1GB limit does impose a limit on RAM, but it is a bit higher.

https://www.redhat.com/archives/rhl-devel-list/2005-January/msg00092.html

A bit of googling indicates that the 4G:4G patch is needed for systems with a lot of RAM (eg. 32 GB or more) because the kernel memory tables scale with the size of physical memory and a 32 GB system uses 0.5 GB for the table, half the kernel space available to a 3G:1G system. A 64 GB system won't boot because all of kernel memory is needed for the table.

The 4G:4G patch is another thing, but you can probably ignore it, it's not in mainline Linux.

It sounds like this limitation has also been overcome, as it is now possible to enable CONFIG_HIGHMEM64G (on i386, i.e. 32-bit). Probably best not to rely on this. Or think too hard about what it must be doing.

2. The direct mapping is not strictly needed for page tables.

Many popular tutorials and walkthroughs for writing an OS, use a mind-blowing trick called "recursive page tables".

https://www.google.co.uk/search?q=recursive+page+tables

Linux didn't use this approach, so traditional Linux is simpler to understand. The direct mapping of the ~1GB "low memory", is set up in the initial page table, and is never changed. And the page tables are allocated from within "low memory".

(Are you thinking about what CONFIG_HIGHMEM64G does now? Stop that, it's bad for you.)

I imagine Linus simply didn't think of the recursive trick. IIRC there are other disadvantages of not having a nicely sized direct mapping available too, but I'm not sure about specific examples.

I say "traditional Linux". I haven't heard if KPTI has actually been merged for 32-bit yet... but anyway, KPTI shouldn't change the broad idea. Once you switch from the user to the kernel page tables, the kernel can access the direct mapping. The switching process is some awesome black magic, but it is simply performed on each context switch. The userspace page tables don't include the direct mapping, but userspace doesn't and shouldn't access the page tables etc., so it's all fine.

  • If it does, then isn't that quote completely wrong? – David Davidson Jul 5 '18 at 19:15
  • @DavidDavidson no. see extra point 1. kernel needs the page tables to be mapped all the time. So that is one example of "memory it must touch directly." – sourcejedi Jul 5 '18 at 19:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.