freopen(NULL, "w", stdout);I mean, it's cute that it's in principle possible, but what does it actually do that can't be achieved more cleanly in other ways?
Also known as file handles referencing file objects (on Windows). Unix terminology is unnecessarily confusing in this place IMHO.
On the subject of the article itself: why was this change introduced? To give kernel support for reopen(3) since dup(3) already exists?
Honestly, the OS situation is a mess and I just want to not think about it, but not having an OS is not really an option.
That we use the term "file descriptors" for pointers from userspace to any kernel object, even those that are not files, is unfortunate, but ultimately just a naming quirk. Windows has a better name, "Handle", but the concept is exactly the same.
The OS includes the file system, and file systems include a notion of paths, and relative paths are really useful. So, the OS helps you by automatically resolving relative paths to your current directory, instead of forcing every application to manually keep track of this.
Linux is perhaps the only popular OS whose interface is not defined in C. All syscalls are clearly documented at the assembler level in Linux, and kept backwards compatible. All other popular OSs (Windows, MacOS, FreeBSD) have a C lib you have to dynamically link if you expect compatibility.
Even if signals weren't a thing, you'd still have to worry about processor interrupts. There is no such thing as a purely single-threaded program on any gpCPU released in the last 30+ years.
The variety of calls in Linux to handle various kinds of events is unfortunate. Windows has a slightly cleaner interface, though even there it's not ideal. Hopefully io_uring will subsume all of the current use cases.
The numbers after the syscalls are related to the man pages where they are documented. Not all that relevant.
Sycalls are not functions, they are specific APIs that the kernel provides to userspace, defined at the assembler level (you put this value in this register/stack and jump to this address/invoke this CPU interrupt). It is up to your language to wrap syscalls into functions, which may have an entirely different calling convention. A kernel can't provide APIs as language-specific functions, as Python's calling convention is vastly different from Haskell's.
Fork() has many meanings that are not related to cutlery, used in CS in other places. Fork() is also an extraordinarily terrible interface for process creation for reasons which have nothing to do with its name. I would be happy if one day Linux gets rid of this insanity and adds a CreateProcess syscall that doesn't have to pretend to copy the entire address space of the current process.
fork() is going to exist forever, but posix_spawn() already exists:
Also some of your specific concerns are impossible to resolve. Take SIGNALS for example. They're ostensibly just callback functions / events. It's very easy to do event-driven programming if all of your events are being raised by the same language runtime as the code you're writing your application in, but how do you raise an event that crosses application boundaries and where your application code would be written in a different language to the event bus (the kernel in this instance)? You ultimately end up with some kind of IPC ugliness and the best solution in the 70s was SIGNAL. Given its now core to the OS, stripping out SIGNALs from Linux would be as easy as stripping HTML from websites.
There are plenty of radically different successors to Linux though. But they all have their own rough edges too. Ultimately these things are complicated and you always end up making compromises somewhere.
You can program in another language other than C and avoid using GNU libc, or Musl libc, or any other libc, so avoid using the C API to talk to the kernel. Other languages like Rust and Go provide their own runtimes and avoid using the C runtime for syscalls. Syscalls are written in assembly language, or at least syscall(2) itself is because the kernel API is just marshalling and a context switch. You misunderstand.
Oh, and the fork(2) function on Linux is implemented by a libc using the clone syscall(2). The (2) is the chapter in the manual providing the documentation. You misunderstand.
> You can program in another language other than C and avoid using GNU libc
Linux is a set of various system interfaces. Many of which are exposed through libc functions. If you deliberately exclude libc and program something parallel to it instead, then you aren't really using Linux. You've created a hybrid system. This only reinforces OP's complaint about not being able to program in a different language, because, in other words, this means that to C language programmer Linux is available fully, and to other languages the functionality is not completely available.
Take, for example, async I/O, which is implemented in libc around other system primitives, s.a. threads. If you don't use libc, you don't have async I/O, and by extension, you don't have Linux, since Linux is supposed to have that.
cpu% ./dup -proc > out; cat out; echo
1d
cpu% ./dup -dup > out; cat out; echo
1d
The slightly modified code can be found here: http://okturing.com/src/18632/body
This seems to be another case of Linux attempting to emulate Plan 9 design and not quite hitting the mark. In general these operating system interfaces are much more consistent and sane within the Plan 9 environment at (seemingly) every turn. I think a lot of folks see the implementation quirks of Linux and decide its time to toss the baby out with the bath water, but studying Plan 9 really shows how nice things could have been.
What do you think about io_uring? I believe it moves away from OS interface as (C) function calls.