einpoklum
einpoklum

Reputation: 131646

What is the compelling use case for the FD_CLOEXEC flag?

I've just read this:

What does the FD_CLOEXEC fcntl() flag do?

and I understand what FD_CLOEXEC does, but not why it's important. Why not just close all the relevant file descriptors before exec()ing something? And why is it so critical to set FD_CLOEXEC that there's a special O_CLOEXEC open mode flag you can set (on Linux 2.6.something and later), only to avoid a bit of inter-thread synchronization?

Upvotes: 4

Views: 1978

Answers (2)

Jay-Pi
Jay-Pi

Reputation: 428

Assume parent process P has a certain amount of files it shares with child process C1 (call it S1) and another amount of files it shares with child process C2 (call it S2). This requires to NOT use CLOEXEC for those files with the open call or whatever other call is used.

After the parent process opened the shared files S1, it calls fork/vfork/clone with the corresponding flags to inherit file descriptors. The same happens for the shared files S2. However, C2 now has also all file descriptors of C1.

Subsequent child processes C3...Cn would all inherit the file handles of previous processes, which is a problem if one works with many files and they are not closed shortly after.

One could now

    1. close those open file descriptors in the child process, which requires to iterate through all file descriptors and close the unknown ones by the child process
    1. let the Kernel deal with that logic and save potentially many slow system calls

The only exception, where file descriptor inheritance is the expected behavior are standard streams, because the user wants to handle child process output of errors or status reports.

Upvotes: 0

EnticingCanine
EnticingCanine

Reputation: 78

I have a use case, albeit one from a toy program (actually a homework assignment).

The assignment was to write a program that would take two arguments, which are the names of executables. It would start them up, using fork and exec (or e.g. execve), with the stdout of the first program writing to one end of a pipe that was read as stdin of the second program.

Example usage: ./mypipeprogram ./program1 ./program2 should give results identical(ish) to ./program1 | ./program2.

I wanted my program to be as close to bulletproof as I could get it, and that meant handling all the errors that I could, as gracefully as I could.

You'll have to forgive me, as I don't think I have the source anymore, so this is from memory.

One of the kinds of errors I had to try to handle was the case where one of the programs listed couldn't be exec'd, but the other could. In that case, the process that couldn't exec could kill the other process, so it wouldn't hang forever, or anything like that.

In hindsight, probably not the best idea, because closing the pipe should have worked fine.

Still, going one way (I think it was the first process, the output one), I could catch this, and instead kill the other process before it even tried to exec the other program.

What I did was open another pipe, and fnctl it with FD_CLOEXEC, relying on POSIX saying that "the file shall be closed upon successful execution of one of the exec functions."

Then the second process would prepare to exec, read from that pipe, and when it was closed, it would know the first process successfully exec'd, so it could go ahead. If the first process's exec failed, the pipe would still be open, so the second process would still be blocked on the read, and the first process could kill it.

TL;DR

I used FD_CLOEXEC on a pipe to allow one process to wait for a second process to successfully exec, or get killed by the second process if the exec failed.

Upvotes: 1

Related Questions