A user level kernel for non-preemptive multiprocessing
A user level kernel for non-preemptive multiprocessing on various 64 bit o/s's.[1]
Code can be found at https://github.com/mahtin/kernel64/ and should be cloned locally in order to run. No precompiled binary is provided.
The project home is the https://kernel64.net website.
[1] Also some 32 bit o/s's - armv6l/armv7l (older Raspberry Pi's) and Intel i386.
Code can be found at https://github.com/mahtin/kernel64/ and should be cloned locally.
All platforms (except i386) should build with:
$ git clone https://github.com/mahtin/kernel64.git
$ cd kernel64
$ make
For i386 platform, cross compile on Intel x86_64 via:
$ git clone https://github.com/mahtin/kernel64.git
$ cd kernel64
$ make M32=-m32
For an armv6l compile on an armv7l host, use:
$ git clone https://github.com/mahtin/kernel64.git
$ cd kernel64
$ make TARGET_ARCH="-march=armv6 -mfpu=vfp -mfloat-abi=hard"
Test routinges are provided via the test*.c files.
Linking to your own code simply means including libkernel64.a in your compile/linker command.
The kernel64.h include file is needed by your calling C source code.
With massive respect to John Lions and his books about the Unix v6 kernel, (which taught me tons back in the 80's), I quote this seminal piece:
/* You are not expected to understand this. */
However, this code is simpler; but, it thinks in the same way (but as modern code running at user level).
The k64init() call should be called first to enable the code.
The core idea is that a process can call k64spawn() to start a process.
That process can either exit cleanly or call k64exit() to finish/return.
As this is a's non-preemptive system, all processes should be polite and call k64yield() often.
Additionally, a process can call k64nice() to adjust it priority (0-255 with 0 being the highest priority).
Once all processes are setup, calling k64schedule() will kick everything off.
Each process is provided with its own stack and if the processor and operating system provide, a guard band is added to the stack in order to detect stack overflow. The internal scheduler also double-checks the stack for both overflow and overwrite. If a process exceeds its stack allocation, it will be destroyed by the scheduler.
Timers and sleep/wakeup operations are also provided.
Read the full Theory of Operations page.
/* Basic calls */
void k64init(int debug);
struct proc64 *k64spawn(const char *name, void (*entry)(void *), void *arg, size_t stack_size, void *v);
void k64exit(void);
/* Yielding is vital as this is a non-preemptive os */
void k64yield(void);
/* Pausing is like a yield; however, at least one clock tick need to happen before resuming */
void k64pause(void);
/* Starting the scheduler */
void k64schedule(void);
/* process priority functions */
void k64nice(proc64pri pri);
void k64renice(struct proc64 *p, proc64pri pri);
/* delays, sleeps, and wakeups for timing or interrupt processing */
void k64delay(size_t msecs);
void k64sleep(void *event);
void k64wakeup(struct proc64 *p);
/* maliciously destroy a process */
void k64destroy(struct proc64 *p);
/* random functions */
struct proc64 *k64pid_to_proc(proc64pid pid);
int k64has_a_process_to_run(void);
size_t k64process_count(int incl_zombie);
size_t k64stack_used(struct proc64 *p);
/* emulating the classic ps command */
void k64print_ps(void);
There's a build in ps command which can show the state of all the process:
PID * STATE NAME PRI STACK USED ENTRY ARG
1 * R [idle] 255 16384 3040 0x104b4cf08 0x0
2 Z procA 80 16384 3120 0x104b4c7c8 0x16b2b2f80
3 Z procB 80 16384 3120 0x104b4c7c8 0x16b2b2f70
4 Z procC 80 16384 3120 0x104b4c7c8 0x16b2b2f60
5 D procS 80 - - 0x104b4c9a8 0x0
The * shows which process is presently running. The state can be R for running, Z for zombie, D for destroy, S for sleeping, plus others. The stack usage is down for running processes. The entry and are are how the process is spawned.
Presently tested on three platforms via GitHub actions:
No RaspberryPi CI is setup presently.
An automatically generated CHANGELOG is provided here.
As always, open an issue or pull request should you want. Edits are welcome.

(Totally AI generated - not a copyright/trademark in sight!)
Code is as-is and should mainly be used for education and testing purposes. Don't run anything critical using this code. Don't run in production.
This code was written with the assistance of AI (mainly in the asm code for stack manipulation); however, I can categorically state that AI didn't get correct its first or second time around. The end result is a code base that performs correctly.
I.e. Don't train an AI on this code.
Copyright (C) 2025-2026 Martin J Levy - W6LHI/G8LHI - @mahtin - https://github.com/mahtin