Writing a scheduler for Linux in Rust that runs in user-space
arighi.blogspot.com
arighi.blogspot.com
"Scheduler activations: effective kernel support for the user-level management of parallelism" https://dl.acm.org/doi/10.1145/121132.121151
and also:
"Adding Scheduler Activations to Mach 3.0" https://dada.cs.washington.edu/research/tr/1992/08/UW-CSE-92...
People put a lot of time into thinking and researching this sort of thing. It even made it into Solaris. For one reason or another (possibly a lack of sufficient hardware parallelism) it never really gained much traction.
Another critical issue to solve is that to do this "properly" (at least based on our model of this from the 90s), you have to have a re-entrant context switch routine, which is an interesting challenge.
isn't this is more of a extreme micro/exo thing where the monolithic scheduler is herniated out into userspace?
And yes, this is a more extreme thing, but what's the point unless there's some capability in user space that can't exist in the kernel? And what would that thing be? Application-specific knowledge allowing better scheduling decisions to be made.
1. block a thread AND
2. allow the task that owns the thread to retain use of the CPU
so when a thread (in user space) calls write, it ends up in the kernel, and the kernel in a traditional design would put the thread to sleep waiting on the write, invoke the scheduler to decide what should run next.With SA, the kernel first assesses whether the task can continue to use the CPU, and if so, calls the task's user-space scheduler so that it can decide which thread to run (since the user-space scheduler has better information about this).
Green threads are unknown to the kernel, they play no role in such a design.
Reentrant context switching is required because you can be in the middle of a context switch invoked from the user space scheduler when a h/w interrupt (incl. timers) causes the kernel to rethink what is going and rip the CPU away from the task.
The amount of information required to correctly decide which task should get the core is too large to sensibly move across the kernel boundary, and providing read-access to it in a safe way from user space is problematic.
The kernel interface that the article uses (called sched_ext) is the result of the attempts to mainstream the Google thing.
SA means upcalling to user-space when thread scheduling is required.
sched_ext means loading a scheduler (coded in BPF) into the kernel.
Not dissimilar goals/purposes but wildly different systems.
Or if you starved your scheduler process because it wasn't high enough priority :/
I think SA takes it a bit too far though. Even if it looks like a 'logical progression of them older ideas.'
What does this look like specifically? Is it that the kernel operation needs to copy memory that is in "kernel space" to/from "user space" for security reasons? Is that all?
Hence the "scheduler activations" concept from the early 1990s, in which the kernel calls into user space with sufficient information for a user space scheduler to make the best possible decision (in theory).
I think we still haven't realized the optimal design, but these are probably good local maxima and points for further exploration.
Since then, the cost has (in absolute terms) gone down, the number of applications for which this is true has not really increased.
In addition, there has been some expansion in the number of applications for which SCHED_RT and SCHED_FIFO are appropriate for at least some threads (financial services high speed trading, real time audio), which has also reduced the pressure for a user-space scheduler that "gets it right". This is also true for designs which require guaranteed scheduling slots.
So, SA might still bring some benefits to large, highly-threaded monolithic apps such as RDBMS, but it doesn't really provide much to the majority of contemporary applications. It certainly doesn't do much for mobile environments, which tend not to run applications where "the application knows best".
It'd be more of a scheduler framework that can be used to support various workloads dynamically.
That's what I'm questioning. Precisely because the dimensions of the problem space have increased, and the raw performance of everything except register save/restore and TLB invalidation has increased, there's wide latitude for scheduling algorithms that are "not quite right".
> while the communication of tasks happen using the bpf() syscall, accessing the queued and dispatched maps.
> NOTE: we could make this part more efficient by using eBPF ring buffers, this would allow direct access to the maps without using a syscall (there’s an ongoing work on this - patches are welcome if you want to contribute).
The kernel piece needs to produce information that the user space consumes and it doesn’t use a 0-copy ring buffer yet.
Because kernel scheduling has context and different architectures must be considered etc.
Also, because context switches are good?
goroutines and Java virtual threads are a separate idea. The application saves its state and then yields back to a scheduler in the application.