vlad9486 wrote: ↑Sat May 03, 2025 6:43 am
My PMM uses bitmap to track allocated pages. I am about to implement shared memory and I need to track what is shared. Can I use reference counter? Instead of one bit for page I can use two bits it represents number between 0 and 3 and serve as reference counter. Is it used by anyone in practice? Why have I never heard about it?
While a PTE might have spare bits for use by the OS, I tend not to use them for portability reasons. Different architectures can have differences in what bits are available for system use, and can't be relied on.
Case in point, how many bits do you have available in an x86 PTE? And how many bits do you need to count references to that PTE?
Shared memory is a concept of virtual memory, and as such, should be handled at the virtual memory level, not at the physical memory allocation and mapping level.
So I hide completely the format of the PTE from the rest of the kernel, and implement copy on write and shared memory entirely in platform independent VM code and data structures that have no knowledge of how virtual address are mapped to physical addresses.
Each allocated physical page has a corresponding VM page structure, which contains the physical page number of the allocated page, a count of copy on write references, and various flags which represent the state of the page (referenced, dirty, pinned).
So all the virtual memory code, entirely written in C, works only with the abstract VM page code to handle VM operations like sharing and copy on write, and defers the actual mapping to opaque platform specific methods that map a VM page to some virtual address.
As an added bonus (in my eyes, anyway), the physical data structures used to implement the mapping become entirely transient, and can be discarded as needed, because they can be entirely recreated from the platform independent data structures.
As an example, I'm aiming my kernel to be able to scale down to platforms with small amounts of memory, and my kernel can operate with multiple address spaces using a single page table in the extreme case, discarding mappings whenever we switch address spaces. Not ideal or optimal for runtime, and for small memory limited systems, you might only be discarding two or three page tables worth of mappings on an address space switch, which for a single user process, would never happen anyway.
On the flip side, I can also scale up the number of concurrent address spaces I retain data for, so a working set of processes can switch address spaces without discarding their existing mappings, up to the limit implemented in the kernel.