Well, it’s needed to happen for quite some time, and now I have finally begun the Great Memory Mapped File Overhaul of 2013 in Pedigree!
The past memory mapped file support, whilst excellent and very functional, was very file-oriented and was particularly complicated to make work for anonymous memory mappings or things that didn’t quite back onto a real file. I have tried to make the new interface support both anonymous and file-oriented mappings, and this is particularly helpful now as anonymous memory maps in Pedigree now use a shared ‘zero’ page (as per conventional operating system kernel design). Previously, anonymous memory maps were mapped and allocated in full at the time of mapping.
Rewind there a moment - you’re asking what on earth these mapping types are?
- An anonymous memory map is a mapping that is not backed by any file or disk storage. That is, it is purely within memory, and this memory is conventionally zeroed. This is often a very quick and easy way for an application to get a hold of a large amount of already-zeroed memory that will only be paged in when it is needed. With madvise() and other such system calls, the application can even inform the operating system that it is done with pages for now, allowing that memory to be released until the application traps and pages in physical pages again. Most modern userspace heap allocators use anonymous memory maps for large allocations. The term ‘anonymous’ refers to the fact that the mapping is not linked to a file.
- File-backed memory mappings are mappings that are backed by file/disk. A trap on a file-backed memory mapping brings the data in the file into memory, and if the mapping is created to be shared, writes to the memory region may be written back to the backing file. For regions of zeroed memory, where anonymous memory maps are not used (or unavailable), /dev/zero or /dev/null can be used. Memory mapped files can be particularly useful in this case to avoid the overhead of memory copies during I/O. For example, a web server might memory map the files it is serving, and pass the memory mapped region to I/O system calls directly, rather than keep the files in a local heap buffer.
For both memory map types, there are circumstances where they can each be paged out of the physical address space, freeing memory. Pedigree has recently had a great deal of work done to make caches compact when the system runs out of memory - the new memory mapped files should also make it possible to free up some memory by cleaning up memory mappings.
An example of such a circumstance might be a file that has been mapped, read, and written to. The kernel can force a memory sync back to disk on the region (ideally at a ‘high water mark’ rather than actually during an out-of-memory situation), and then start freeing physical memory in an LRU (least-recently-used) fashion. Because memory mapped files pin pages they use in the caching subsystem, being able to force a sync and release of this pin is very, very handy, and was not trivial to complete in the previous implementation.
The latest memory mapping work has now made it possible to create a /dev/fb device for graphics, rather than requiring the use of native system calls to get a hold of a framebuffer. This is particularly useful for other developers interested in developing desktop environments for Pedigree, as it should soon be possible to expect a reasonable amount of Linux compatibility on the /dev/fb device. Once this is implemented, it will be possible to write a desktop environment for Pedigree (in combination with the rudimentary UNIX datagram socket support) that can be tested on a Linux environment, and be immediately portable to Pedigree. Ideally, this would greatly improve the testing cycle time for these userspace elements.
The new support still needs some work, of course:
- The window manager currently aborts inside dlmalloc, potentially due to a failed mmap.
- Support for mmap flags is minimal at best, and this definitely needs to be resolved.
- Cleanup is relatively untested and may be leaky - the true test will be running an application multiple times and confirming that the memory usage on the system does not grow.
- No syncing of shared file mappings is done yet, and was not done in the past implementation either. This is not a huge deal, but it would be very nice to be able to trigger a write back on the memory region (assuming it had actually changed). Upon write back completion, the pages can be mapped read-only back to the file’s backing cache (rather than copies made during writes) - great for memory usage!
Hopefully the work done here will also be of particular use in eventually implementing a swap subsystem for Pedigree, which can be used to free up memory by writing it back to disk. There are certainly a number of processes which would benefit from this, with pages that barely get touched during their execution. This would also be a great step towards being able to support suspend-to-disk (ie, hibernation) in the future.
So there’s still much to be done, but there’s much being done as well - Pedigree is moving forwards. Once this memory mapping work is completed, things should be looking good to ramp up to another Unstable release for testing. So that’s exciting :)