Here's a Unix question for you. Should opening /dev/fd/N act as if you'd dup()'d that file descriptor?

In retrospect I should have run this poll for 12 hours instead of 24, because I can't give my answer until the poll closes. In the mean time, I'm mostly sitting on my hands here.

Yesterday I polled people on whether opening /dev/fd/N should act as if you'd dup()'d that file descriptor. The answer is 'no, that's terrifying'.

When you dup() file descriptors, the two fds share a bunch of state, especially including the IO offset in the file; meanwhile, open() promises a file descriptor with independent state. So turning open() into dup() may cause all sorts of fun surprises as changes to the current offset and other things on one fd are magically also on the other.

@cks Makes sense as a way to save/branch from some state but wow this is cursed.

@lanodan I'm actually a bit curious about whether there's a deep reason for that dup() behavior or if it started as an artifact of an easy implementation (just increase the reference count on a file object in the kernel, no need to copy it and do stuff and etc etc). Especially since early Unixes had small limits on things like total open files; dup()'s sharing reduced pressure on them.

@cks @lanodan another reason is that some types of files can be opened only once (e.g. pipe).

@robryk @lanodan I guess there may be internal kernel state for pipes and so on that assumes the file object will be unique and never duplicated.

(Even if the kernel provides no user-level way to re-open a pipe, it can internally duplicate their file objects.)

PS: It turns out you have to have dup() semantics to make shell pipelines that redirect to/from files work, so my speculation is wrong anyways.

Follow

@cks @lanodan

I don't mean that implementation requires that, but that I don't see what semantics you could assign to two independent opens of the same end of the same pipe.

(Actually, the problem is even worse with Unix sockets: server side was not created on request, so you can't really do anything other than moral equivalent of dup; client-side was not created via open() *and* the file/abstract name that was dialed could no longer be accessible.)

@robryk @lanodan Pipes and sockets are already sort of special, since you can't seek on them. However there are probably some attributes that are useful to set separately on instances of pipes (and sockets), like O_NONBLOCK (although I can't remember if that was in V7, not that sockets were).

@cks @lanodan

Ah, I see, you are proposing something that would still function as the "same open" for all intents and purposes but not share flags (or file pointers, but you mentioned that this would break appending to shared std{out,err}). I think I agree that sharing of O_NONBLOCK and other flags I can think of makes no sense.

I wonder why O_NONBLOCK is a FL-flag and not an FD-flag.

Sign in to participate in the conversation
Qoto Mastodon

QOTO: Question Others to Teach Ourselves
An inclusive, Academic Freedom, instance
All cultures welcome.
Hate speech and harassment strictly forbidden.