Confusion regarding VMMs, and allocations of less than 4KB
Posted: Tue Mar 24, 2015 5:58 pm
I'm getting very confused about how to manage my virtual address space. I've gotten my head wrapped around paging, and mapping, and I'm now trying to figure out how to implement a malloc function that will allow me to create allocations that are less than 4KB in size. As it stands now, I have a good physical memory manager based off a bitmap, and a good page fault handler. When something tries to read from, or write to, a virtual address that isn't mapped the page fault handler will check the bitmap for a free page of physical memory, map it to the appropriate virtual address, and allow things to continue happily along their way. I'm wanting to get things set up so I can move in to multitasking, and allow each process to have its own page directory so it'll have a unique virtual address space. I'm not really sure how to proceed with this. I've previously written a first fit memory manager for a flat memory model that kept track of which address ranges were free using a linked list. I thought that a virtual memory manager would be similar. I'm getting conflicting information from multiple sources, and I'm hoping the forum can give me a definitive explanation of what's going on. Some sources are saying the kernel can't make allocations that are less than 4KB in size, others are saying that's what malloc is for. One place is telling me to set up a heap, and another is saying that heaps are an obsolete concept because we now have paging. I'm also quite confused about the concept of kmalloc, and what that's all about.
Here's my paging code.
Here's the header file for my paging code. The definitions at the bottom are functions I'm thinking about adding.
Here's a header I'm working on for my virtual memory manager.
I'm writing a higher half kernel for a 32-bit system with non-PAE paging. I followed the Higher Half Bare Bones Tutorial to get the higher half stuff set up, and when the kernel runs my paging initialization function I switch things over to 4KB paging. I'm setting up one page directory, and one page table, for the kernel, getting the bitmap set up, and getting recursive mapping set up. Since I'm programming for a 32-bit system I know I'll have a theoretical maximum of 4GB of physical memory, and used that as an upper limit for the size of my bitmap. I have the page directory and page table in the physical memory that's right after my kernel. The bitmap gets set up in the physical memory right after the page table.
Here's how the virtual memory mappings for my kernel are set up:
0xC000 0000 => PD[768] = mappings for the kernel, kernel page directory, and kernel page table.
0xFFB0 0000 => PD[1022], PT[768] = mappings for the bitmap for the physical memory manager.
0xFFC0 0000 => PD[1023] = recursive mappings.
What I've been considering doing for the virtual memory manager is creating a linked list to keep track of which virtual addresses are free. Each node on the list would store a pointer to a free virtual address, and a value telling the virtual memory manager how large the free section is. Whenever something calls malloc I would iterate over the list to find a portion of virtual memory address space large enough for the allocation, manipulate the list to keep it current, and return a pointer to the beginning of the allocation. I've already done something similar (before I got in to paging) for a flat memory model, and the code for that can be seen here. The linked list would occupy some random location in physical memory, but be mapped to 0xFF80 0000. As the virtual memory addresses get fragmented more "free" nodes would be added to the list, and it would "grow" towards the bitmap's virtual address range. Nodes of adjacent free addresses would occasionally be compacted together to limit the growth of the list. I am slightly concerned that as the virtual address space becomes fragmented the list could clobber the bitmap by growing into its virtual address range, but there are ways to prevent this from happening. Basically, the list would occupy random pages of physical memory as needed, but use a continuous range of virtual addresses. Each process would have a separate page directory, separate list, and separate recursive mapping, but the kernel and bitmap mappings would remain constant for every page directory. By doing this each process would be spawned with its own virtual address space that was unique to itself, and could make arbitrarily small allocations of virtual address space on demand. No pages of physical memory would be mapped until something actually tried to write to a virtual address, and then the page fault handler would take care of it. I'm thinking of calling all of the virtual addresses below 0xC000 0000 "user space," and everything above that "kernel space." The kernel would manage the task switching by keeping some information about each process mapped in kernel space, and use that to determine which page directory (or virtual address space) to switch to once a process's time slice had expired.
I sure this will work as I'm intending, but worried that I'm missing some large concept related to virtual memory management. Does anyone have a better explanation of the concept? Are there any big "gotchas" I'm missing?
Here's my paging code.
Here's the header file for my paging code. The definitions at the bottom are functions I'm thinking about adding.
Here's a header I'm working on for my virtual memory manager.
I'm writing a higher half kernel for a 32-bit system with non-PAE paging. I followed the Higher Half Bare Bones Tutorial to get the higher half stuff set up, and when the kernel runs my paging initialization function I switch things over to 4KB paging. I'm setting up one page directory, and one page table, for the kernel, getting the bitmap set up, and getting recursive mapping set up. Since I'm programming for a 32-bit system I know I'll have a theoretical maximum of 4GB of physical memory, and used that as an upper limit for the size of my bitmap. I have the page directory and page table in the physical memory that's right after my kernel. The bitmap gets set up in the physical memory right after the page table.
Here's how the virtual memory mappings for my kernel are set up:
0xC000 0000 => PD[768] = mappings for the kernel, kernel page directory, and kernel page table.
0xFFB0 0000 => PD[1022], PT[768] = mappings for the bitmap for the physical memory manager.
0xFFC0 0000 => PD[1023] = recursive mappings.
What I've been considering doing for the virtual memory manager is creating a linked list to keep track of which virtual addresses are free. Each node on the list would store a pointer to a free virtual address, and a value telling the virtual memory manager how large the free section is. Whenever something calls malloc I would iterate over the list to find a portion of virtual memory address space large enough for the allocation, manipulate the list to keep it current, and return a pointer to the beginning of the allocation. I've already done something similar (before I got in to paging) for a flat memory model, and the code for that can be seen here. The linked list would occupy some random location in physical memory, but be mapped to 0xFF80 0000. As the virtual memory addresses get fragmented more "free" nodes would be added to the list, and it would "grow" towards the bitmap's virtual address range. Nodes of adjacent free addresses would occasionally be compacted together to limit the growth of the list. I am slightly concerned that as the virtual address space becomes fragmented the list could clobber the bitmap by growing into its virtual address range, but there are ways to prevent this from happening. Basically, the list would occupy random pages of physical memory as needed, but use a continuous range of virtual addresses. Each process would have a separate page directory, separate list, and separate recursive mapping, but the kernel and bitmap mappings would remain constant for every page directory. By doing this each process would be spawned with its own virtual address space that was unique to itself, and could make arbitrarily small allocations of virtual address space on demand. No pages of physical memory would be mapped until something actually tried to write to a virtual address, and then the page fault handler would take care of it. I'm thinking of calling all of the virtual addresses below 0xC000 0000 "user space," and everything above that "kernel space." The kernel would manage the task switching by keeping some information about each process mapped in kernel space, and use that to determine which page directory (or virtual address space) to switch to once a process's time slice had expired.
I sure this will work as I'm intending, but worried that I'm missing some large concept related to virtual memory management. Does anyone have a better explanation of the concept? Are there any big "gotchas" I'm missing?