5 ms·
This is an absurd interpretation of "everything is a file." The kind that makes you go "arghwhat?!" A file descriptor is exactly nothing like a file. You canno
by mike_hock 3y ago
This is an absurd interpretation of "everything is a file." The kind that makes you go "arghwhat?!"
A file descriptor is exactly nothing like a file. You cannot write to a pidfd. You cannot waitid an eventfd. You cannot getsockopt on a regular file. The only operations that all file descriptors have in common is close, dup, poll and some other basic operations.
So "file descriptor" basically just means "kernel interface object" and the available operations depend on the type of object.
A file is a container for arbitrary data that I can read from, write to, and reposition the read/write cursor in. If you call anything else a "file" then you haven't made everything a file, you've just redefined "file" to mean "thing."
What business does a socket have in a physical, on-disk filesystem? (Let alone a clunky hack to invoke the much simpler `signal` syscall in a roundabout way?) The socket "file" is completely meaningless unless the process that opened it is currently alive and still listening on it. So why the fuck should it get written to a persistent storage device?
How do I specify the socket type, which is a meaningless concept for an actual file, when I open a socket "file"? Oh that's right, I don't. Because I don't open a socket. I bind or connect. I don't use "file" APIs because they're not applicable. I use a dedicated socket API that's fit for the purpose.
Pidfd was not introduced to treat processes as "files," it was introduced so they could share the operations that they do meaningfully share with other kernel objects (e.g. poll).
The overloaded ioctl syscall is bad design. The proc "filesystem" is bad design. /dev/shm is ridiculous design. So I have to mock a fake filesystem in memory so I can create a fake file in that "filesystem" just so I can get the same memory pages mapped into my virtual address space as some other process, all of which has absolutely nothing to do with files or a filesystem (and is much lower level than that). lolwat?
- codedokode 3y agoAlso, I remembered, there is /dev/pts, a weird file-based API that you need to use to create pseudo-terminals.
- arghwhat 3y agoA file descriptor is a handle to a file, and anything you have an fd to is a file. This file carries a vfs implementation, such as that of pidfd, a device driver, or disk storage. The kernel does not distinguish between these. If not being able to write makes it not a file, then files stop existing when a disk is full, and means that /dev/zero and /dev/null are not files - despite being at the heart of the whole "everything is a file" paradigm. Pidfd was not made to make processes behave like files - that is what /proc is - but to solve problems with process related syscalls and PIDs, which are flawed and racey. The solution to that was to make APIs that treat processes as files, which gives you the ability to poll it like a file. A streaming socket is exactly like a normal file. You read, write and poll. The only thing that is special is how to create it, but that is a design decision, not a technical limitation - see the plan9 file based API for making TCP sockets, which is trivially implementable in Linux. Domain sockets are a bit different because of their side channel and would require more ctl files, but Linux's API is 99% magic files and ioctls so this is not that weird. ioctls are not themselves bad design. In fact, scoping kernel functionality onto file handles is a great design and why that's almost the entirety of the kernel (device driver calls dwarf syscalls). The problem is not the design itself, but the fact that ioctl was not originally meant for it and got overloaded through several design iterations. This is what happens when you do organic design through more than 3 decades. If you start out by defining a way to do file-scoped syscalls - and file does and always will mean "an fd" to a kernel - then you wouldn't have that awkwardness. That is what plan9 did: Take the learnings, and implement them clean instead of on legacy.
- mike_hock 3y ago> A file descriptor is a handle to a file, and anything you have an fd to is a file. This file carries a vfs implementation, such as that of pidfd, a device driver, or disk storage. The kernel does not distinguish between these. Which is what I said. It's a "file" in name only. > If not being able to write makes it not a file, then files stop existing when a disk is full, and means that /dev/zero and /dev/null are not files - despite being at the heart of the whole "everything is a file" paradigm. Great example that showcases the idiocy of Everything Is A File. /dev/null and /dev/zero are basic parts of the Unix API, so the basic OS API is broken-by-default at boot until one mounts a file system that had these dummy "device" nodes at a specific path that's hardcoded everywhere. Instead of providing a sensible API like memfd_create or timerfd_create, for example. > A streaming socket is exactly like a normal file. You read, write and poll. That doesn't make it a file, that makes it an object that shares common traits with file objects. Datagram sockets do not read/write because they're not bound to a fixed remote address. And that's perfectly fine. They're not files. > The only thing that is special is how to create it, but that is a design decision, not a technical limitation And it's a good design decision. The socket API is pretty decent except for the dumb "file" nodes it creates when listening on standard unix sockets. > see the plan9 file based API for making TCP sockets, which is trivially implementable in Linux. But thank God it's not implemented in Linux. > ioctls are not themselves bad design. In fact, scoping kernel functionality onto file handles is a great design Agreed, the only bad part is that too much was shoehorned into the same syscall. It's certainly better than magic "files" that pretend to be "files" by having you read and write structs from, but you're only allowed to read and write whole structs per syscall, which has no resemblance whatsoever to how reading from and writing to a file work. (Maybe Plan9 doesn't have this limitation and tries harder to keep up the charade, I wouldn't know.)
- arghwhat 3y ago> Which is what I said. It's a "file" in name only. No, it is the very definition of a file from the OS perspective. There is no other applicable definition to the OS. You seem to conflate files with disk storage, in which case not just plan9, not just Linux but the entirety of UNIX history seems to have flown past you. That files are a nothing but abstract handles that implements the VFS interface to serve every conceivable function - where regular file is treated no differently than a device driver - is the entire point of modern UNIX. If this is the part you are stuck on it is not a surprise that both the existing Linux kernel APIs and trivial (and quite frankly, perfectly ergonomic and efficient) alternatives like the plan9 API seem so foreign to you. "read and write structs" is the most normal thing for an application to do, whether you are reading JSON from disk, communicating over UNIX domain sockets with raw C structs, or sending protobuf over the network. "But thank God it's not implemented in Linux" - Linux has many of these APIs already and it constantly grows, see for example all of /dev, /sys and /proc, not to mention FUSE and support for the 9P protocol to use all of plan9's services as-is.