File descriptor handling changes in 2.6.27
Posted Aug 9, 2008 23:43 UTC (Sat) by jlokier
In reply to: File descriptor handling changes in 2.6.27
Parent article: File descriptor handling changes in 2.6.27
the code that creates the fd is (often)
the code that is best prepared to know whether it should be kept local to the process or not.
I would modify that in multi-threaded programs. Code is best prepared to know whether its descriptors should be kept local to the process or passed to child processes it creates itself. Other threads, which may run unrelated code also doing fork+exec at the same time, should not pass the same descriptors to their child processes.
Any code (say in a utility library that you don't control yourself) that does fork+exec, may create a pipe or something to pass to its child process. It knows the descriptor should not be local to the process.
Trouble is, another thread can be doing something completely unrelated. So to be safe, all code including utility libraries must use O_CLOEXEC (or equivalent) for every descriptor they create, and later call FD_CLOEXEC inside the child after fork() to turn off close-on-exec, the opposite of what's normally done.
The other alternative is to have a global lock around all calls which create file descriptors and fork(). That's fine in code you control, and completely portable. But you can't expect all utility libraries to cooperate. Even gethostbyname() won't cooperate.
Another alternative is to close all possible file descriptors after fork() except those being inherited. But that's slow, sometimes very slow, and you still need all utility libraries which use fork() to do that themselves.
It's ugly however you look at it. Utility libraries are unlikely to do the right thing for a long time, if ever. You certainly can't trust them to do the right thing unless they explicitly document that they do, or unless you know for sure they don't create file descriptors.
(Btw, what I do in my "big server" app is a combination of the above: close unknown descriptors, but keep track of calls into utility libraries, assume a limit of the number of descriptors they each open at a time, and using knowledge of the POSIX first-free-number rule, close that many total descriptors that my app doesn't know about explicitly, so it's not too slow, and use O_CLOEXEC or lazy-F_CLOEXEC to manage descriptors that the app does know about explicitly. It's ugly, but wraps into a tidy enough API and scales well.)
to post comments)