Information on implementing L4

John john.r.moser at
Fri Sep 14 13:44:19 CEST 2018

On Fri, Sep 14, 2018 at 3:42 AM Zenaan Harkness <zen at> wrote:

> On Fri, Sep 14, 2018 at 09:00:25AM +0200, Gábor Wacha wrote:
> >    Hello,
> >    I do not really want to start a flamewar about JIT and AOT, but AFAIK
> >    using a JIT can introduce nondeterministic behavior (in runtime,
> >    performance), which is not really wanted in a kernel. Am I correct?
> >    Gabor Wacha
> Mainly JIT is associated also with runtime garbage collection or GC.
Which is actually interesting in an OS-level JIT because you can do fun
things like garbage collect based around LRU pages, use memory protections
to track access, and so forth.

On one hand, it's possible for a compacting GC to allocate additional
physical RAM and compact in that, marking pages as read-only in the target
program (service) before starting to rewrite them.  If the service accesses
a page involved in GC to write, then the service can fault to the GC
service, which will make note and correct the protection on the page
(relying on fast IPC and a fast page fault handler for this path).
Meanwhile, the GC is actually writing the rewritten pages to
newly-allocated physical RAM.

In the end, the GC settles at a consistent state (potentially
partially-collected) at a time when each of the modified pages is read-only
in the target service.  It can then identify strongly-connected components
to find the fewest pages which, when replaced, result in a consistent state
for the service.  For each such set, it first moves through to remove all
permissions on each page (unless it's faster or the same to handle a page
fault to an unmapped page), which is still interruptable by handling the
page fault on access and putting permissions back; and then makes a second
pass to replace all of those pages, handling page faults in this phase by
replacing the particular page in question.

This form of garbage collection has the same impact on the kernel service
being collected as swapping its pages out to RAM (that is:  unmapping the
pages from the process, but not doing anything else with them except
mapping them back in upon access).

You can stick to LRU pages for active processes, and even garbage collect
any pages going in and out of swap if you're doing that with kernel
services.  Really, you're going to need to GC active pages at some point,
and can target processes which are sleeping.

> JIT can even happen ahead of time just like a normal compiler, but
> not with profiling (which must necessarily run at run time or with
> test/ sample data), but when JIT happens at runtime, later execution
> of a JITed code path will be quicker than the first execution(s) of
> that same code path, depending on how the JIT is configured to run
> (either first time always, or only after a path is taken say 10 times
> etc).
With the same considerations as above:  no need to stop the world for the
whole process; you rewrite a shadow copy of the pages, then turn them
over.  At any stage, you're one page fault away from simply executing in a
consistent state, and you can fight the uphill battle of getting all
relevant pages unmapped at once as long as you want.

These JIT and GC strategies give full priority to performance:  the service
being altered keeps running, and in the worst case hits an unmapped page
and triggers a page fault.  That page fault is satisfied immediately by
mapping the correct page and continuing execution.  In theory, it's fastest
to have the GC itself handle that mapping; in practice, you'd likely have
VM notify GC of the fault (synchronously) and then take action, which makes
this not the fastest pagefault.  There may be other ways to achieve a
minimum-performance-impact page fault in this scenario.

Obviously, idle CPU becomes GC and JIT CPU, so this all eats more

> l4-hackers mailing list
> l4-hackers at
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the l4-hackers mailing list