I like to go back at the drawing board. Gives you opportunity to
think, to look at what you have, and to reconsider what you need.
The MH's memory map system (pmap) move to machine independent part
– similar to the change that did this for the CPUs and IPIs
– offered such opportunity for this key component.
Compared to other memory subsystems I have seen in the
I always thought of MH's virtual memory to be extremely simple. So,
before blindly porting a code that shows signs of ageing and where
different strata starts emerging, I decided to look at the states of
a virtual mapping. Afterall, I expected it to be simple.
The first layer was easy to draw:
I.e., a user PTE is either unmapped, or mapped to a hardware I/O page,
or mapped to a memory page. The dot points to the default state, that
is at start we expect all virtual memory to be unmapped.
Of course, it's not that simple. Mapped pages can be wired by the
kernel, i.e. made unable to be unmapped by userspace programs. The act
of wiring a page is still initiated by the user, by exporting a
memory to a hwdev. Wiring is needed because exporting a page to a
hwdev allows doing DMA on that memory region.
An updated graph will look like this:
Which is – still – very simple. The square represents what's left of
the node called Mapped in the earlier figure.
But this is ignoring something really important: copy-on-write. MH
supports forking of processes. As essentially any other OS who
supports paging, it uses copy-on-write as an optimization for
duplicating address spaces: instead of being copied, the memory is shared
but set read only.
When a process tries to write to a shared page, it will generate a
page fault, which will be forwarded as an interrupt to the process. It
is userspace responsibility to create a new page, copying its content,
and substitute the read-only shared mapping with a writable copy.
This then means that this state, RO Shared, can only be changed by
userspace by unmapping it. There is an exception of course, which is
due to an optimization: when all processes but one have all created
their own copies, the page is effectively not shared anymore, and can
be re-made private.
An important note to add at this point is that copy-on-write is the
only case in MH where memory is shared between processes.
Updating the graph yields:
This starts to look interesting, and messy enough to look real. It is
not complete, yet.
As said earlier, the read/write information is lost during the process
of copy-on-write, so in the current implementation the page returns
into being private, but read-only, no matter the original state. This
is not a problem, though, as long as the userspace process can handle
unexpected RO page faults by fixing them back to writable. The MRG
system library supports this.
Adding details about R/W state brings to a more complete graph, with
clear signs of state explosion:
Is this complete? Of course not. We're missing information about the
executable state of a page. A page can be executable or not, and as
long as it is private and not wired its state can be changed.
I won't display this, though, as it is essentially an orthogonal state
with regards to the lifetime of a page.