dup2 / dup-为什么我需要复制一个文件描述符?


85

我想了解使用dup2dup

从手册页:

DESCRIPTION

dup and dup2 create a copy of the file descriptor oldfd.
After successful return of dup or dup2, the old and new descriptors may
be used interchangeably. They share locks, file position pointers and
flags; for example, if the file position is modified by using lseek on
one of the descriptors, the position is also changed for the other.

The two descriptors do not share the close-on-exec flag, however.

dup uses the lowest-numbered unused descriptor for the new descriptor.

dup2 makes newfd be the copy of oldfd, closing newfd first if necessary.  

RETURN VALUE

dup and dup2 return the new descriptor, or -1 if an error occurred 
(in which case, errno is set appropriately).  

为什么我需要该系统调用?复制文件描述符有什么用?

如果我有文件描述符,为什么要复制它?

如果您能解释一下并举一个需要dup2/的示例,我们将不胜感激dup

谢谢


没有dup或时如何实现壳的管道功能dup2?您需要致电pipe(2),然后拥有一个文件描述符dup-ed例如STDIN_FILENO
Basile Starynkevitch 2012年

Answers:


46

dup系统调用复制了一个现有的文件描述符,并返回了一个新的文件描述符,该描述符引用了相同的基础I / O对象。

Dup允许Shell执行以下命令:

ls existing-file non-existing-file > tmp1  2>&1

2>&1告诉外壳程序给命令一个文件描述符2,它是描述符1的副本(即stderr&stdout指向相同的fd)。
现在,在tmp1文件中显示了在不存在的文件上调用ls的错误消息以及在现有文件上的ls的正确输出。

以下示例代码使用标准输入连接到管道的读取端运行程序wc。

int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = 0;
pipe(p);
if(fork() == 0) {
    close(STDIN); //CHILD CLOSING stdin
    dup(p[STDIN]); // copies the fd of read end of pipe into its fd i.e 0 (STDIN)
    close(p[STDIN]);
    close(p[STDOUT]);
    exec("/bin/wc", argv);
} else {
    write(p[STDOUT], "hello world\n", 12);
    close(p[STDIN]);
    close(p[STDOUT]);
}

子进程将读取端复制到文件描述符0上,关闭p中的文件描述符,然后执行wc。当wc从其标准输入中读取时,它将从管道中读取。
这就是使用dup实现管道的方式,好吧,现在使用dup一次使用管道来构建其他东西,这就是系统调用的美,您可以使用已经存在的工具来构建另一件东西,这些工具又是使用等等。最后,系统调用是您在内核中获得的最基本的工具

干杯:)


1
那么dup对调用者有帮助,而不对ls程序本身有帮助吗?dup如果已经可以访问文件,则可以像ls本身那样在程序中使用过吗?例如,在这里,ls将错误2编码为硬编码,因此我有一种方法可以替代它作为消费者ls。我认为那是一个微妙的点,不是吗?
Nishant

2
您的示例程序似乎存在错误;您在打电话,dup(p[STDIN])但是扔掉了结果。您是要使用dup2(p[STDIN], 0)吗?
Quuxplusone

1
@Quuxplusonedup返回“该进程当前未使用的最低编号的描述符”。由于fd 0刚刚关闭,dup应该返回0。dup2它明确指出了应该使用哪个fd,而不是仅使用最低的空闲fd,因此我更愿意这样做。
Wodin

@沃丁:啊,我敢打赌你对OP的想法是正确的。我是否也说对了,“仅关闭”是相对的,并且OP的代码可能在存在并发线程(例如也可能打开文件)的情况下中断?
Quuxplusone

@Quuxplusone我怀疑您是对的,但不确定。但是在那种情况下,您将遇到其他问题。如果因为要从其他地方读取而关闭stdin,然后另一个线程在执行此操作之前先打开文件,则它将获得fd0。如果然后使用dup2,则它将关闭另一个线程打开的fd,因此另一个线程现在将读取(并写入?)打开的文件。无论如何,如果我没记错的话,您不应exec*从多线程进程中调用。但是我不是线程专家:)
Wodin

18

复制文件描述符的另一个原因是将其与一起使用fdopenfclose关闭传递给的文件描述符fdopen,因此,如果您不希望关闭原始文件描述符,则必须先复制它dup


fdopen()似乎不复制文件描述符,它只是在用户空间中创建缓冲区。
埃里克·王

3
你看错了我的答案。关键是您可能需要dup先将fd传递给fd,fdopen因为fclose它将关闭它。
R .. GitHub停止帮助ICE,2015年

1
@ theferrit32:如果您分配了一个FILE句柄以通过stdio接口访问一个预先存在的打开文件,则需要调用fclose以释放该FILE句柄。如果您想继续使用基础打开文件,或者您的软件体系结构使得文件描述符的原始“所有者”代码可以使用close,那么fclose也将关闭您传递给基础文件描述符的事实fdopen。您可以使用dup来为传递给的同一打开文件制作新的文件描述符来避免此问题fdopen,这样fclose就不会关闭原始文件。
R .. GitHub STOP HELPING ICE

1
关键是fdopen()fd的所有权移至FILE,而不是复制。这是用户应该注意的事情。fd除了FILE对象之外,还需要保留可用句柄的消费者必须复制fd。就这样。
康拉德·迈尔

1
@ConradMeyer:是的,这是放置它的一种很好的方法,并请注意,FILE一旦将所有权转移到所有权上,就无法进行“转移所有权”的操作。
R .. GitHub停止帮助ICE

4

dup用于重定向进程的输出。

例如,如果要保存流程的输出,则复制输出(fd = 1),将复制的fd重定向到文件,然后派生并执行流程,并在流程完成时再次重定向将fd保存到输出。


4

请注意与dup / dup2有关的一些要点

dup / dup2-从技术上讲,目的是通过不同的句柄在一个进程内共享一个文件表条目。(如果我们是分叉的,则默认情况下,子进程中的描述符是重复的,并且文件表条目也是共享的)。

这意味着我们可以使用dup / dup2函数为一个打开的文件表条目提供多个具有不同属性的文件描述符。

(尽管目前看来只有FD_CLOEXEC标志是文件描述符的唯一属性)。

http://www.gnu.org/software/libc/manual/html_node/Descriptor-Flags.html

dup(fd) is equivalent to fcntl(fd, F_DUPFD, 0);

dup2(fildes, fildes2); is equivalent to 

   close(fildes2);
   fcntl(fildes, F_DUPFD, fildes2);

差异是(最后一个)-除了dup2和fcntl close之后的一些errno值之外,fcntl可能紧随竞争条件,因为涉及两个函数调用。

可以从http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html检查详细信息

使用示例-

在shell中执行作业控制时的一个有趣示例,在下面的链接中可以看到dup / dup2的使用。

http://www.gnu.org/software/libc/manual/html_node/Launching-Jobs.html#Launching-Jobs

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.