Experimental OS, built with Rust
output.mp4
Fun fact: there are 3 apps running in the video. A background app, a cursor app, and a console app.
I wanted to experiment with Non-Unix OS ideas.
Exo-kernels are interesting, but it is mostly a theory. This project helps me understand the challenges involved in that pattern.
OS development is extremely hard, Rust makes it more bearable.
- Has a graphical output
- Dynamic allocation
- Load and run concurrent apps
- All apps run in an async loop
- Support Virtio mouse and keyboard (drivers are async tasks)
- Cooperative scheduling (apps yield control as much as possible)
- No context switches once booted
- Nearly support Virgl ™
There is 5 examples of apps in this repo named app_*
, some in Rust, one in C.
The kernel is in bootloader
.
The signature of an app in Fomos:
pub extern "C" fn _start(ctx: &mut Context) -> i32
Apps do not need a standard library, any OS functionality is given to the app through the Context.
the Context is mostly a pointer to a bag of kernel functionnalities
In Fomos, an app is really just a function. There is nothing else ! This is a huge claim. An executable for a Unix or Windows OS is extremely complex compared to a freestanding function.
It is out a frustration for all my Glibc problems during day job dev on linux that I chose to try this approach.
I want a flat contract between an app and the OS. So what if an app was a function ? The contract is then only the explicit argument type.
In Unix, an app has to know the target OS, but also what standard library it uses, that is 2 levels of indirections. Sometimes the os level has a conflict, sometimes the standard library level has a conflict, and sometimes I just don’t have the level to understand why something doesn’t work. I merely know it is related.
I am trying to know if it is possible to have an OS-App ecosystem that does not suppose ANY implicit configuration. An app would JUST have to handle its explicit start
context argument.
Context gives any OS function necessary, think alloc, free, access to a framebuffer, or any hardware, any system calls etc.
That way, apps could be freestanding, and compatible on multiple OS.
More about Context
Here is the Context for the last version of this OS
Note that app_test
for instance, uses an old version of the Context, and still works on the newer version of the OS
Old Context used by app_test
:
Meaning Fomos already handles gracefully Apps designed for a much older version of itself. As long as the OS stays compatible with the old stuff in the context, it can add new functionalities for other App by just appending to the context the new functions (here calloc, cdalloc, store, and input).
app_test
precedes the dynamic allocation age !
Could that pattern work in the long term ?
How about system calls
None. Lets try to put everything into Context functions. No voodoo cpu instruction magic.
But how do you give back control to the OS ?
Just
How do you sleep, or wait asynchronously ?
Just
Apps are cooperative in Fomos, They can just return (which would exit permanently an app on a classic OS), and assume that they are gonna be called through their only function start
again soon, maybe even instantly if the “system call” works that way.
But an app loses all RAM data everytime it yields that way !
No, an app can store anything it wants in Context.store during its execution, and get it back every start
call. The OS keeps everything in RAM (on the heap). The stack itself is “reset”. But it is not more “reset” than it is after any function execution in a normal program. You don’t lose anything. In Fomos, apps are merely a single function called multiple times!
Over simplification of the kernel loop:
loop { for app in apps.iter_mut() { app._start(Context::new(...)); } }
There are a lot of questions without answer yet, but by now you might be curious, what if all the question had an answer in the pattern ? It looks like it could actually work (with a lot of work).
A lot of stuff comes free once you accept the premises.
Sandboxing, instrumentation, debugging
Every functionnality and side effect given to an app goes explicitely through the Context. The Context is just a struct, we can wrap or replace anything in it.
Lets instrument an app we’ll cal