I’ve had an idea I’ve been wanting to build into an OS for ages - where we use versions, message passing and structured patches to pass information between the kernel and application processes. So an application can subscribe to a file / USB device state / list of running processes / anything using a common, standard API. But it sounds really daunting to try and hack something like into the Linux kernel or something in one go.
If I want to experiment with something like this, what do you recommend? How hard would it be to do something like that in a hobby kernel like Axle?
Thanks for the kind words! I'll echo rajeevk's recommendation: the OSDev wiki is full of useful documentation and guides for setting up a bare-metal toolchain. There's also a community forum with a wealth of knowledge just waiting to be read.
In terms of boots on the ground, I remember once seeing a mantra from someone on the OSDev forums that went something like this: building an x86 OS is first about getting the CPU into a usable state, then about getting as far away from it as possible. This is really true! There's a lot of work to do to set up the features that any modern environment needs as a baseline: paging, segmentation, interrupt descriptors, etc, but once you're done with all that you're free to roam in the design space and build whatever you like, however you imagine, building abstractions and then abstractions on top of those.
I'd liken the early stages of OS development to a tech tree in a video game: you need serial output so that you can debug your kernel. You need to be able to allocate memory so that you can create pixel buffers, then need drawing primitives to fill them up. You need processes, and a scheduler to coordinate them. Every new feature you add opens up a myriad of possibilities that weren't there before, and although many things have a serial list of dependencies, there's always so much to do that you can just focus on whatever feels particularly inspiring to you at any given time. It's really a lot of fun.
Thanks for the links and encouragement! I suppose my main hesitation is that it seems like an awful lot of work to build all that from scratch. I really want to - it looks super fun. But I've got my hands totally full at the moment building a CRDT & data engine for local first software.
I really just have one idea I want to explore, which is this idea around sharing state between the kernel and userland via subscriptions and patches. (By taking some ideas from event sourcing and things like that). I guess I'm trying to figure out where I would even start prototyping something like that. I guess I could just fork axle and hack on it a bit to see how it goes?
You can probably also hack into some of those educationnal OSes that already have a workable base, i.e xv6, os161, maybe even minix ? This way you wouldn't have to implement the initial tedious layers yourself.
I wrote a unix-like kernel from scratch long back. It was not a fully functional unix but had a lot of components ready to the extent it would provide a command line interfaceI from which you can run commands like ls, cat etc by loading elf executables.
Linux exposes system information like processes and devices in the psuedo file system hosted at the mount point /proc. Any user mode programs can use the standard file IO API to access them. Some of the "files" like /proc/self/mounts are pollable, i.e. a program can use blocking select to wait for changes like when a new file system has been mounted.
Yeah, I know how it works in linux. I just hate it. Polling and writing a parser for a pseudo-file always seems like a super roundabout, inefficient, brittle way to send information from kernel to userspace.
Think about it - the kernel stores some data in a nice, neat struct. And the kernel knows exactly when that struct is modified. Userspace wants that data, and wants to know when the data changes. So every second, userspace "reads" a pseudo file. The kernel packs the struct's contents into a big string with some sloppy, hand written text format and passes it to userspace. Then in userspace you need to write a brittle parser for whatever the kernel gives you, knowing that in different versions of the kernel you'll get slightly differently formatted strings.
When the data changes in the kernel, the application has no way of being notified. It just polls and parses in a loop. Its both inefficient (when there are no changes, its all wasted effort). And its laggy (when changes happen, it'll take your application up to the polling interval to find out about the changes).
For things like filesystem watching, there are special APIs applications can consume. But those APIs are different for every kind of data the kernel manages. Devices, mount points, USB connectivity, filesystem watching, CPU usage, thermal sensors, open network connections and on and on. All of this stuff has totally different kernel-level APIs for "watching" changes. (If you're lucky - a lot of it can only be polled). Its a big fat mess. I want to try replacing all of that with a nice clean API for fetching & subscribing to any kind of data. So, the kernel has a standard API for fetching (maybe it returns JSON). And a standard API for subscribing to changes - such that your application is notified when a change occurs, and the change is sent in a standard, easily digestible patch format.
And ideally, eventually, I'd like applications to be able to share their own changes back with the kernel in the same form & format. And maybe, share data objects between applications like that - with the kernel acting as a broker for sending patches back and forth, and syncing them with the filesystem. I think that would be really neat and useful for all sorts of applications.
But I want to prototype it all first in something thats not linux. I'm just not sure where to start. I don't really want to write an OS from scratch just to try out this generic state passing idea.
In Unix everything is a file. Linux is not Unix but tries to be like Unix. This all files represent a resource on the system. It’s hard to separate this from POSIX requirements. Lose POSIX compatibility and you end up with a half implemented system like Windows. And then you must recreate the entire application ecosystem yourself or you end up with this funky BeOS/Haiku style POSIX compatibility layer that excludes a good subset of applications that already exist.
PS /proc isn’t a file system, it is an api (that can be accessed by the file system)
Right, so it's an API, but it's only usable through the file system, and it's a pretty bad API. You still need to parse the crappy ad hoc format instead of getting a struct from the kernel.
If it were in Linux it would probably make sense to keep both interfaces, for the time being. Existing apps can use the current interface, and newer apps can opt in to a fancier interface / api.
I agree with you mostly. In some cases the performance might not be desirable to deliver the events. One minor nitpick, pollable endpoints on /proc means that a program can run select() or epoll() on the file descriptor. Those can be blocked to sleep, no need to re-poll at intervals.
Windows has WMI and other systems have SNMP MIB as a uniform interface to access system resource. Those never get anywhere.
I don’t see why performance would be worse. Presumably, the application would register an event handler to get change events. And optionally block until an event arrives just like you can do now.
My expectation is performance would be better because you don’t need to parse the data as a string.
And you also avoid the ABA problem this way. If you wait for a file in procfs to change, by the time you read it it may have changed to something else - and you’ve lost data.
I've been pondering this for a moment and if I were to write an application for such a system I don't think I'd enjoy it. If you want to pass information from the kernel and bubble that all the way to the application to do something with it, I would think you now have to write signal handling in your application.
Obviously, the signal handling code can live in a library of some sort.
With a generic, system wide api there’s no reason you couldn’t still poll if you really wanted to. But maybe at least with a version token. So the application can say “I want info on which processes are running. Last time I asked, you gave me token 5827275” and the kernel can respond and say “you’ve got the most recent version of that data already champ”.
I’ve had an idea I’ve been wanting to build into an OS for ages - where we use versions, message passing and structured patches to pass information between the kernel and application processes. So an application can subscribe to a file / USB device state / list of running processes / anything using a common, standard API. But it sounds really daunting to try and hack something like into the Linux kernel or something in one go.
If I want to experiment with something like this, what do you recommend? How hard would it be to do something like that in a hobby kernel like Axle?