Asynchronous non-copying message passing
Well I decided to shelve work on the MMU code for now and focus on easier targets. I implemented the basic functions for message passing first. Although since I turned off the memory protection things are quite simple - it just passes around pointers for the most part. The idea for enabling memory protection would be to have each 'message' allocated page aligned in a global virtual address range, and just re-map the page to the target process address space when it is received or replied to, thus enforcing the rules of who owns the memory when; although any data the message references is another issue. I could force messages to include all data they reference (they have a length field), and perhaps special-case io request's buffers. If i force those semantics, then it could probably allow for a copying implementation to be written as well.
Ports are stored inside kernel memory and referenced by handle, rather than by address in user space - so they can go away without fatal problems and can't be corrupted. One problem is that I will probably need to add another query system call to get a copy of objects like these as they contain information a task might want to access - like the signal bit used. Probably useful for other privileged system objects anyway, like library bases or tasks. Anyway, I managed to send messages between tasks 'in the normal way'.
I also had to write some TagList utilities - and I added a new one which can 'pack' a taglist into a structure. Somewhat inefficient but it'll do for now.
And i'm starting to think about how the various `kernel' interfaces might work in practice. Basically everything is accessed through (globally) shared libraries with a few system calls thrown in when necessary. But some of the code wont need to run in supervisor mode at all or go to another server.
So what is the next thing to look at ... well I decided to look at devices, and again checked out the AmigaOS implementation. It's a little different from microkernel models I've read about - io requests can be handled on the user context, for example. And apart from that they are actually implemented as shared libraries - complete with the possibility of public functions directly callable. But long-running i/o functionality runs in a separate task and may interact with interrupts and so on - like a normal uKernel approach.
In a protected environment things will have to change, although the basic ideas seem quite sound and usable.
But before I can get that far there's a lot of mucking about to do. I need to work out the design of the process and thread model and how they'll start and finish.
And how to do libraries; devices are just libraries too. Shared libraries will be objects, not just function tables, and in a virtual memory environment this creates some issues. e.g. should it allow system-wide shared data, per-user shared-data or just per-process/thread state. Also do I want to enforce `pure' re-entrant code for libraries - which saves the hassle when working out what to do with the data segments.