What are the rules of closing file descriptors after calling dup/dup2?
Asked Answered
C

2

7

I feel like this is a topic I've taken for granted. In the past I literally just closed as many file descriptors "because I was told to". Most of the time this worked, but occasionally I ran into some unpredictable behaviour.

Thus, I'd like to ask - what the rule for closing file descriptors after calling dup / dup2?

Let's say I want to perform cat < in > out.

fd[IN] = open("in", O_RDONLY);
saved_stdin = dup(STDIN_FILENO);
dup2(fd[IN], STDIN_FILENO);
close(fd[IN])

fd[OUT] = open("out", O_WRONLY | O_CREAT | O_TRUNC, 0644);
saved_stdout = dup(STDOUT_FILENO);
dup2(fd[OUT], STDOUT_FILENO);
close(fd[OUT])


// Later on when I want to restore stdin and stdout
dup2(saved_stdin, STDIN_FILENO);
close(saved_stdin);
dup2(saved_stdout, STDINOUT_FILENO);
close(saved_stdout);

Is this correct or should I be closing more file descriptors?

Chaddie answered 20/4, 2019 at 7:59 Comment(0)
F
2

The rule is indeed quite simple. For both dup() variants, it is true, that:

  • The source fd remains open and will have to be closed once it is no longer needed.

  • The target file descriptor,

    • when using dup(), is always an unused one
    • when using dup2(), is implicitly closed and replaced by a copy of the source fd.
  • The new target fd has to be closed, when it is no longer needed.

Source fd refers to the file descriptor to be duplicated, while target fd is the new file descriptor.

int new_fd = dup(source_fd);
dup2(source_fd, new_fd);

So yes, your code does the necessary closes, and no unneeded ones.

Follow answered 20/4, 2019 at 8:20 Comment(5)
Sorry can you clarify what you mean by source fd and target fd?Chaddie
i've a last question - is there any harm in saving stdin/stdout multiple times in a row? e.g. saved_stdin = dup(STDIN_FILENO). If i'm correct this neither closes saved_stdin nor STDIN_FILENO am I correct?Chaddie
This is correct, but the following ` dup2()` closes the previous stdin and replaces it with fd[IN]. That's no problem at all.Follow
I was wondering though, if you do saved_stdin = dup(STDIN_FILENO) followed by dup2(fd[IN], STDIN_FILENO). Now STDIN_FILENO points to fd[IN]. Then if you call saved_stdin = dup(STDIN_FILENO) again wouldn't that replace saved_stdin (which holds original STDIN_FILENO) with fd[IN]? I feel like I'm overthinking this or somethingChaddie
@Chaddie You may not confuse the overwriting of a variable with the overwriting of a file descriptor. Setting a variable does of course not have any side effec on the set of file descriptors associated with a process.Follow
P
0

The figures is come from CSAPP System-Level:

Figure 2: Before Redirect IO

dup2(4,1);

Figure 1: After Redirect IO

Notice the refcnt of fd 1 has changed to 0, after call dup2.

According to the description of close in linux manual. It said:

if the file descriptor was the last reference to a file which has been removed using unlink(2), the file is deleted.

close be used to decrease the refcnt of opened file. we use dup to create a new fd will increase the refcnt of opened file. when we call close function, it did't close the file immediately, it only decrease the reference count of file. the file will be close/delete when the refcnt is 0.

So it's really like the Reference counting for memory management.

Principality answered 8/12, 2021 at 6:13 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.