Search code examples
cposixfile-descriptor

Can file descriptors other than the std in/out/err be defined statically?


As most developers will be aware processes define three file descriptors which we know more commonly as stdin, stdout and stderr.

From what I can tell the fd for each of these are statically defined as 0, 1 and 2 respectively. This is explicitly stated in the POSIX standard: http://pubs.opengroup.org/onlinepubs/009695399/functions/stdin.html

Now lets say I have a group of processes which require a 4th. For example a child process created with fork() exec() requiring a handle to a control socket created with socketpair() before the fork(). For this example the purpose of the 4th handle (socket) is to provide a link between parent and child process. Now the question comes... how does the child know which FD is the control socket? Is there any reason why I can't use a static number for this (eg: #define CONTROL_SOCKET 3) as long as I dup2(new_socket,CONTROL_SOCKET) between the fork() and the exec(). I'd then just be able to foo = write(CONTROL_SOCKET, bar, baz) inside the child. Lets take it as read that any other FDs open my app are expected to close on exec so my logic is that dup2() won't close anything that won't be closed anyway by exec().

I know that there are several possible work-arounds to avoid doing this (eg: passing the FD in an environment variable or program arguments) and there are a number of examples on the web showing how to do so. What I don't understand is what is being worked around? What's the problem with statically defining an FD that's being avoided? It feels at first glance to be avoiding a problem which doesn't exist. Do some systems use FDs other than 0, 1 and 2 and might I be overwriting something important by dup2(<some fd>,3) before an exec?

Note: This question is really a question about writing code which is portable between existing POSIX operating systems.


Solution

  • There is no widespread use of file descriptors other than 0, 1, 2. I seem to remember a few tracing systems using a fixed file descriptor out of that range (though not usually 3), but they were very much the exception (and I don't remember noticing it this millennium).

    On the whole, if the first process in the set is able to ensure that the descriptor was not in use when it was started, it can safely pass the descriptor on to its child processes. If you find the descriptor already in use, you could open a different descriptor to the control socket, and then use fstat() to compare the two descriptors. If they're for the same device, you can probably go ahead using the inherited descriptor 3; if not, you need to dup2() and close() the control socket.

    If your processes plan to simply open the control socket themselves, there's no particular need to worry about which file descriptor is used.