Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
I'm working on a dynamic memory manager for my homebrew OS. It's for Intel, 32bit mode, paging is on (PAE in my case). And I have the following "conflict of interests": I want to disable writing to the code pages, but dynamic memory allocation/deallocation needs the writing to be enabled.
The most obvious solutions that I can imagine are:
Rejecting the idea of disabling writing at all (but it's not safe)
Allowing writing only at the moments when allocation/deallocation happens and only for the pages which are involved (not clear for me how much it will affecte the perfomance)
The question is: how this problem is solved in the modern OSs? What are the most popular approaches?
I'm working on a dynamic memory manager for my homebrew OS. It's for Intel, 32bit mode, paging is on (PAE in my case). And I have the following "conflict of interests": I want to disable writing to the code pages, but dynamic memory allocation/deallocation needs the writing to be enabled.
The most obvious solutions that I can imagine are:
Rejecting the idea of disabling writing at all (but it's not safe)
Allowing writing only at the moments when allocation/deallocation happens and only for the pages which are involved (not clear for me how much it will affecte the perfomance)
The question is: how this problem is solved in the modern OSs? What are the most popular approaches?
I'm confused.
Why would dynamic memory allocation require writing to code pages?
I'm working on a dynamic memory manager for my homebrew OS. It's for Intel, 32bit mode, paging is on (PAE in my case). And I have the following "conflict of interests": I want to disable writing to the code pages, but dynamic memory allocation/deallocation needs the writing to be enabled.
The most obvious solutions that I can imagine are:
Rejecting the idea of disabling writing at all (but it's not safe)
Allowing writing only at the moments when allocation/deallocation happens and only for the pages which are involved (not clear for me how much it will affecte the perfomance)
The question is: how this problem is solved in the modern OSs? What are the most popular approaches?
I'm confused.
Why would dynamic memory allocation require writing to code pages?
Depends on the allocation algorithm in use. I use First-Match algorithm in the form described in the "The Art of Computer Programming" by Knuth. This is a double-linked list of memory blocks with systme fields adjacent to the allocated memory. I believe buddy allocation algorith also keeps the system fields adjacent.
Anyway, even if a way exist to keep the system fields in the data pages, loading for example a process executable into the memory requires writing to the code pages.
On the other thought, let me reformulate my question in a more general form.
I wonder how existing professional OSs deal with the dynamic memory allocation for the code, having paging active. In particular, when lots of small dynamic libraries are loaded/unloaded at arbitrary moments.
If the kernel needs write access to load the shared libraries into those pages (which it may not, thanks to DMA), it can map them as inaccessible to userspace while it loads them, then change the permissions to be read-only when granting userspace access. On x86, you can use INVLPG if you need to revoke the kernel's write access immediately.
Shared libraries may require some in-memory modification (relocation) after loading before they can be used. On systems that use ELF, these modifications are handled in userspace, and the ELF format ensures that executable pages will not need any modification.
Octocontrabass wrote: ↑Sun Mar 30, 2025 12:42 am
If the kernel needs write access to load the shared libraries into those pages (which it may not, thanks to DMA), it can map them as inaccessible to userspace while it loads them, then change the permissions to be read-only when granting userspace access. On x86, you can use INVLPG if you need to revoke the kernel's write access immediately.
The DMA is still a thing to learn for me. Modifying the permissions is what I thought about too. At the beginning I was worrying about the perfomance, but finally I realized that executables are not loaded/unloaded several times per second. So, it should be ok.
iansjack wrote: ↑Sun Mar 30, 2025 1:32 am
Surely page allocation and process loading are done, via system calls, at supervisor level?
In this case the granularity of the memory blocks for the code will be at least 4 KB. I wonder if there are researches on how much memory is wasted in this case (on Linux or Windows, for example).
I don’t know what you mean. Page allocation, and protection, is always a minimum of 4K. Whether the kernel allocates pages or you - somehow - make it a user process doesn’t alter that. You don’t want to share code pages between diverse processes.
Of course ther is a small amount of wasted memory, just as there is wasted space on a block-oriented storage device. The memory saved by on-demand paging more than compensates for that.
iansjack wrote: ↑Sun Mar 30, 2025 9:42 am
I don’t know what you mean. Page allocation, and protection, is always a minimum of 4K. Whether the kernel allocates pages or you - somehow - make it a user process doesn’t alter that. You don’t want to share code pages between diverse processes.
Of course ther is a small amount of wasted memory, just as there is wasted space on a block-oriented storage device.
This doesn't answer my question, but ok - no problem, I asked it rather because of a theoretical interest.
The best way to answer your question would be to read one of the many books describing the implementation of the Linux (BSD, Mac OS - take your pick) kernel.