Working on a non-POSIX OS built around a microkernel, I go overboard with thinking about separating the concerns of different services (processes that run in the background).
Drivers are an obvious one - they can be loaded independently and should be swapped out based on the hardware of the computer it's running on, so it makes sense that they're individual processes.
To support drivers, I have a "Device Manager" service that launches drivers, and drivers can query it for the PCI configuration.
I have a "Window Manager" that's self explanatory.
I have a "Storage Manager" that hosts the VFS, and sends the individual commands to the driver of the storage devices.
I noticed that Fontconfig did redundant work with enumerating all fonts on each GUI program that launched, so I moved it into a "Font Manager" service that programs can query, and the fonts are loaded into shared memory, so every GUI program doesn't have to perform disk reads just to draw some text in the standard system font.
I have a "Loader" that loads ELF files off disk and starts executing them. This could have been in a library, but I decided against it because I want Program A to be able to launch Program B, and Prorgam B might have permissions that Progam A doesn't, and so I needed a trusted service to say "this really is Program B I'm creating", and not Program A loading some imposter program and saying "trust me, it's Program B".
Going forward, I think I want to build a "Permissions Manager" that other processes and query and say "Can this process perform this action?" where the action might be access the network, write to disk, etc.
What about functionality like the code that parses "http://" and opens it with a web browser, or a ".jpeg" and opens it with an image viewer? Should this be a library function, it's own service, or live alongside the "loader"?
What about the system settings where the user can do things like change the display resolution and keyboard mapping - should the "Settings UI" be implemented in the running driver itself (super flexible but would require a lot of redundant code), or some kind of "Control Panel" with a standardized UI for configuring the keyboard/screens/speakers/etc regardless of what driver is running? Does the "Control Panel" have to be constantly running (e.g. so an graphics driver can query what resolution to set the display to, and can report back if the user plugged in a different monitor that forced it to change resolution to something else), or do we store these shared settings in a "Registry" service?
Anytime I think there is some logic that either needs to be trusted (can't be in a library), data that should be shared between processes (e.g. the registry), or redundant work that multiple processes would do (e.g. fontconfig), I have a habit of thinking it needs to be in its own service. "You get a service! And you get a service! And you get a service!"
How do others architect their microkernel ecosystems and think about these problems?
And you get a service!
- AndrewAPrice
- Member
- Posts: 2297
- Joined: Mon Jun 05, 2006 11:00 pm
- Location: USA (and Australia)
And you get a service!
My OS is Perception.
Re: And you get a service!
Well, I don't plan to write a microkernel, but some of the concepts are interesting. I now have two very complex tasks as services: The VFS and SSL. The VFS has the most complex design. Each physical disc has a server process, where the disc driver is mapped. Then each partition has it's own process that maps the disc server between 2 and 3 GB. The partition process has the two first GBs for it's own purpose. The SSL is a lot easier and has a single server process
Unlike typical microkernel implementations, I don't have a standardized communication interface. Each service defines it's own way of commuicating between applications and the service. Although I have an IPC mechanism that both the VFS and SSL use, but they currently don't share it, which I eventually might change. The IPC use a single 4k page where registers and data can be shared.
I also have added a new syscall type which only services can use. This way I can protect the service processes communication with kernel so applications cannot use it. I should have defined this syscall interface per service, but I didn't, which I kind of regret now. That's something that I might want change eventually.
The VFS implements full file system drivers, including partition manager and formatter. I used to link formatting code and partition creation into the shell, but with the new VFS design I can disallow application access to these areas.
The SSL link the OpenSsl library to the service, and then starts a server thread per connection with shared receive and transmit buffers. This way the application must use the service interface and cannot mess directly with sockets or OpenSsl.
Unlike typical microkernel implementations, I don't have a standardized communication interface. Each service defines it's own way of commuicating between applications and the service. Although I have an IPC mechanism that both the VFS and SSL use, but they currently don't share it, which I eventually might change. The IPC use a single 4k page where registers and data can be shared.
I also have added a new syscall type which only services can use. This way I can protect the service processes communication with kernel so applications cannot use it. I should have defined this syscall interface per service, but I didn't, which I kind of regret now. That's something that I might want change eventually.
The VFS implements full file system drivers, including partition manager and formatter. I used to link formatting code and partition creation into the shell, but with the new VFS design I can disallow application access to these areas.
The SSL link the OpenSsl library to the service, and then starts a server thread per connection with shared receive and transmit buffers. This way the application must use the service interface and cannot mess directly with sockets or OpenSsl.
Re: And you get a service!
Plan 9 implements this as a service called the plumber. Each user runs a copy of the plumber. It's very convenient because you can change the configuration whenever you want and all programs get the info. (Unless you run multiple plumbers in different namespaces, but that's a Plan 9-specific insanity.)AndrewAPrice wrote:What about functionality like the code that parses "http://" and opens it with a web browser, or a ".jpeg" and opens it with an image viewer? Should this be a library function, it's own service, or live alongside the "loader"?
Plan 9 delegates all authentication tasks, including password negotiation for foreign services such as ssh, to "the auth server"; a service which needn't even run on the same computer as anything else. I don't know if that's any use to you.AndrewAPrice wrote:Anytime I think there is some logic that either needs to be trusted (can't be in a library), data that should be shared between processes (e.g. the registry), or redundant work that multiple processes would do (e.g. fontconfig), I have a habit of thinking it needs to be in its own service. "You get a service! And you get a service! And you get a service!"
Kaph — a modular OS intended to be easy and fun to administer and code for.
"May wisdom, fun, and the greater good shine forth in all your work." — Leo Brodie
"May wisdom, fun, and the greater good shine forth in all your work." — Leo Brodie
-
- Member
- Posts: 510
- Joined: Wed Mar 09, 2011 3:55 am
Re: And you get a service!
On traditional MMUs, I'm not sure there's a good answer.AndrewAPrice wrote:What about functionality like the code that parses "http://" and opens it with a web browser, or a ".jpeg" and opens it with an image viewer? Should this be a library function, it's own service, or live alongside the "loader"?
On hardware with something like the z/Architecture access register mechanism, where a single program can have multiple address spaces active at a time and each address space is a capability, granting access to its own list of address spaces the answer is "a library function in a library with its own private address space for sensitive data", along with basically everything else that's broken out into its own service in a traditional microkernel.
Traditional MMUs basically provide one such private address space (kernelspace) to be shared by all code on the system that needs to have access to data that its callers don't have access to. The only alternative is to break the code out into its own schedulable entity as a service, with its own private usermode address space, even if it doesn't really make sense for it to be a schedulable entity.
Microkernel services and monolithic kernel drivers are generally things that make the most sense as libraries, but require isolation from their callers. On a traditional MMU, it's a case of "Pick any two: Drivers are libraries, drivers are isolated from callers, drivers are isolated from each other.".