Skip to content

3.12 dup and dup2 Functions

An existing file descriptor is duplicated by either of the following functions:

#include <unistd.h>
int dup(int fd);
int dup2(int fd, int fd2);

Both return: new file descriptor if OK, −1 on error.

NOTE:

dupdup2 所copy的是file descriptor,并不file table entry。这是它和open的差异。

With dup2, we specify the value of the new descriptor with the fd2 argument. (下面根据fd2是否open,可以对它的用法进行分类: )

一、If fd2 is already open, it is first closed. If fd equals fd2, then dup2 returns fd2 without closing it.

NOTE:

这种用法是将fd2重定向到fd,这种情况下,会首先close fd2,然后让fd2的file descriptor的file pointer指向fd的file table entry;此时dup2函数执行的功能非常类似于指针赋值。

二、Otherwise, the FD_CLOEXEC file descriptor flag is cleared for fd2, so that fd2 is left open if the process calls exec.

NOTE: 在下面的"File descriptor flag"节中会对"the FD_CLOEXEC file descriptor flag "进行介绍,结合其中的内容可知这段话的意思是: fd2 file descriptor的"FD_CLOEXEC file descriptor flag"的默认值是 "cleared" 的,因此"fd2 is left open if the process calls exec"。

在 stackoverflow Re-opening stdout and stdin file descriptors after closing them 的第一个回答中,有着各加直接的说明:

The two descriptors do not share file descriptor flags (the close-on-exec flag). The close-on-exec flag (FD_CLOEXEC; see fcntl(2)) for the duplicate descriptor is off.

也就是说,dup系列函数是不copy "file descriptor flags"的。

The new file descriptor that is returned as the value of the functions shares the same file table entry as the fd argument. We show this in Figure 3.9.

In this figure, we assume that when it’s started, the process executes

newfd = dup(1);

File descriptor flag

Each descriptor has its own set of file descriptor flags. As we describe in Section 3.14, the close-on-exec file descriptor flag for the new descriptor is always cleared by the dup functions.

fcntl equivalent

Similarly, the call

dup2(fd, fd2);

is equivalent to

close(fd2);
fcntl(fd, F_DUPFD, fd2);

In this last case, the dup2 is not exactly the same as a close followed by an fcntl. The differences are as follows:

1、dup2 is an atomic operation, whereas the alternate form involves two function calls. It is possible in the latter case to have a signal catcher called between the close and the fcntl that could modify the file descriptors. (We describe signals in Chapter 10.) The same problem could occur if a different thread changes the file descriptors. (We describe threads in Chapter 11.)

2、There are some errno differences between dup2 and fcntl.

使用场景

需要搞清楚 dupdup2 的使用场景:

1、dup系列函数可以用于实现IO Redirection,通过Google,我在这篇文章中找到了一段代码,它就是使用的dup系列函数来实现IO Redirection。

chapter 3的exercise 3.4

许多程序都包含下面一段代码:

dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
if (fd > 2)
    close(fd);

为了说明if语句的必要性,假设fd是1,画出每次调用dup2时,3个描述符项及相应的文件表项的变化情况。然后画出fd为3的情况。

解答

If fd is 1, then the dup2(fd, 1) returns 1 without closing file descriptor 1. (Remember our discussion of this in Section 3.12.) After the three calls to dup2, all three descriptors point to the same file table entry. Nothing needs to be closed.

If fd is 3, however, after the three calls to dup2, four descriptors are pointing to the same file table entry. In this case, we need to close descriptor 3.

opengroup dup and dup2 中的说明

在第一次阅读上述代码的时候,我没有搞清楚为什么作者会添加if (fd > 2) close(fd);,看来opengroup dup and dup2 中的解释后才知其中缘由:

1、上述代码作者的目的在于将STDIN_FILENO ,STDOUT_FILENO ,STDERR_FILENO 全部都重定向到fd上,添加上if (fd > 2) close(fd);的目的在于clean up,即将无用的file descriptor全部都关闭掉,这样可以有很多好处,如防止leak to child process;

tag-not leak泄露resource into child process-unintended file descriptor