And you get a service!
Posted: Wed Jan 10, 2024 10:31 am
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?
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?