> Syslets are small, simple, lightweight programs (consisting of system-calls, 'atoms') that the kernel can execute autonomously (and, not the least, asynchronously), without having to exit back into user-space. ....
> Syslets consist of 'syslet atoms', where each atom represents a single system-call. These atoms can be chained to each other: serially, in branches or in loops.
User space memory mapping (naturally left to the user) seems to be the error prone bit here.
search for "sys_umem" here: https://people.redhat.com/mingo/syslet-patches/patches/async...
I understand that this is low level stuff for experienced developers, but this approach basically asks the programmer to play linker. Entirely doable but I wonder what it looks like at scale (LOC).
To be fair, io_uring seems to be a pretty sane design. But, for example, for many years anything with a shared ring buffer between userspace and the kernel was suspect. Now there are several such interfaces. Overall tolerance has grown, especially on the part of Linus. The kernel is much larger, and Linus seems to have become much more deferential to subsystem maintainers as his ability to understand and opine on code has slipped beyond his grasp.
Similarly if all the syscalls done between fork and exec could be squeezed into a single uring chain without any intervening userspace code then the kernel could skip some costly virtual memory operations, bringing us closer to posix_spawn behavior.
And ideally also the cost of mmap_sem (the lock for an address space's memory map), which can be one of the most contended locks in a workload.
> Similarly if all the syscalls done between fork and exec could be squeezed into a single uring chain without any intervening userspace code then the kernel could skip some costly virtual memory operations, bringing us closer to posix_spawn behavior.
I'd love to see a way of doing this with io_uring.
This idea reminds me of pipelined Redis commands. It does work to boost total throughout, but it’s significantly more complicated to use in practice.
1. Enqueue A = openat(...)
2. Enqueue B = read(A, buf, sz)
3. Enqueue close(A)
4. Commit, return B to userspace
A and B are placeholders, and A is never known to userspace, it is simply chained within the enqueued commands.In the case of uring I suppose it is possible to link the calls to fail at the first failure.
That's what the min_fd patch is for, and potentially other systems for reserving blocks of fds. Much like memory, you'll ask the kernel for a block of it and then do your own allocations out of that.
Think about how pipelined commands work in a command shell if you want to understand how to construct a program that can define a sequence of operations on objects before they're defined.
whoops. I probably should have thought of that.
It might work like this: user space allocates a buffer, fills it with syscall numbers and arguments, then passes it to the kernel. The kernel executes each syscall in series, placing the return values in the array originally containing arguments. This syscall would not return to user space until all syscalls in the buffer are processed.
Does this idea have any potential?