Comment by zozbot234
21 days ago
> a lot of things end up having really awkward "ctl" files which you write command strings to that the fileserver needs to parse.
These are no different in principle than ioctl calls in *ix systems. The `ctl` approach is at least a proper generalization. Being able to use simple read/write primitives for everything else is nonetheless a significant gain.
They are very different. ioctl's on a file take an operation and arguments that are often userspace pointers as the kernel can freely access any process's memory space. ctl files on the other hand are merely human-readable strings that are parsed.
Say, imagine an API where you need to provide an 1KiB string. The plan9 version would have to process the input byte for byte to sort out what the command was, then read the string to a dynamic buffer while unescaping it until it finds, say, the newline character.
The ioctl would just have an integer for the operation, and if it wanted to it could set the source page up for CoW so it didn't even have to read or copy the data at all.
Then we have to add the consideration of context switches: The traditional ioctl approach is just calling process, kernel and back. Under plan9, you must switch from calling process, to kernel, to fileserver process, to kernel, to fileserver process (repeat multiple times for multiple read calls), to kernel, and finally to calling process to complete the write. Now if you need a result you need to read a file, and so you get to repeat the entire process for the read operation!
Under Linux we're upset with the cost of the ioctl approach, and for some APIs plan to let io_uring batch up ioctls - the plan9 approach would be considered unfathomably expensive.
> The `ctl` approach is at least a proper generalization.
ioctl is already a proper generalization of "call operation on file with arguments", but because it was frowned upon originally it never really got the beauty-treatment it needed to not just be a lot of header file defines.
However, ioctl'ing a magic define is no different than writing a magic string.
It's perfectly possible to provide binary interfaces that don't need byte-wise parsing or that work more like io_uring as part of a Plan9 approach, it's just not idiomatic. Providing zero-copy communication of any "source" range of pages across processes is also a facility that could be provided by any plan9-like kernel via segment(3) and segattach(2), though the details would of course be somewhat hardware-dependent and making this "sharing" available across the network might be a bit harder.
Indeed, you can disregard plan9 common practice and adopt the ioctl pattern, but then you just created ioctl under a different name, having gained nothing over it.
You will still have the significant context switching overhead, and you will still need distinct write-then-read phases for any return value. Manual buffer sharing is also notably more cumbersome than having a kernel just look directly at the value, and the neat part of being able to operate these fileservers by hand from a shell is lost.
So while I don't disagree with you on a technical level, taking that approach seems like it misses the point of the plan9 paradigm entirely and converts it to a worse form of the ioctl-based approach that it is seen as a cleaner alternative to.
3 replies →