有人能解释一下C语言中的dup()函数是做什么用的吗?

27

我知道dup、dup2、dup3会“创建文件描述符oldfd的副本”(摘自man页面)。但是我无法理解。

据我所知,文件描述符只是用于跟踪文件位置和方向(输入/输出)的数字。直接复制它们不是更容易吗?

fd=fd2;

每当我们想要复制文件描述符时?

还有一些其他的事情...

dup()使用未使用的最低编号描述符作为新描述符。

这是否意味着它也可以将stdin,stdout或stderr作为值,如果我们假设关闭了其中一个?


3
对于第一个问题,你需要记住内核会跟踪打开文件句柄的数量。dup是告诉内核你希望它继续跟踪其中另一个文件句柄(指向同一文件),直到你使用close关闭它。 - user786653
8个回答

25

在做了一些实验后,我想回答一下我的第二个问题。

答案是。如果stdin、stdout或stderr被关闭,你创建的文件描述符可以取值0、1或2。

例如:

close(1);     //closing stdout
newfd=dup(1); //newfd takes value of least available fd number

当这种情况发生在文件描述符上时:

0 stdin     .--------------.     0 stdin     .--------------.     0 stdin
1 stdout   =|   close(1)   :=>   2 stderr   =| newfd=dup(1) :=>   1 newfd
2 stderr    '--------------'                 '--------------'     2 stderr

1
又出现了一个问题:我如何在已经关闭的文件描述符上执行dup()操作? - Pithikos
从定义上来看,dup() 函数使用未使用的最低编号描述符作为新描述符。当您关闭文件描述符时,这意味着它可以被再次使用。 - Raghu Srikanth Reddy

15

文件描述符不仅仅是一个数字,它还携带各种半隐藏的状态(比如它是否打开,它指向哪个文件描述符,以及一些标志位)。dup会复制这些信息,这样你就可以独立关闭两个描述符。但是fd=fd2则不行。


1
这里,“number”是内核维护的数据结构中的索引。dup函数族克隆该数据结构中节点的状态,并将新节点的索引交给您。 - dmckee --- ex-moderator kitten
3
@dmckee: "Kernel"和"index"是实现细节。与"the number"相关的一些数据不能直接被程序操作,并通过dup()复制。这就是程序员需要知道的全部内容。顺便说一下,这不是非常有趣的数据,它只是几个标志和对真正有趣的数据(打开文件描述符)的索引,这个数据不会dup()复制。 - n. m.

7
假设你正在编写一个shell程序并且想要在运行程序时重定向标准输入和输出,可以像这样实现:
fdin = open(infile, O_RDONLY);
fdout = open(outfile, O_WRONLY);
// Check for errors, send messages to stdout.
...
int pid = fork(0);
if(pid == 0) {
    close(0);
    dup(fdin);
    close(fdin);
    close(1);
    dup(fdout);
    close(fdout);
    execvp(program, argv);
}
// Parent process cleans up, maybe waits for child.
...

dup2()是一种更方便的方法,用于替换close() dup()的操作:

dup2(fdin, 0);
dup2(fdout, 1);

你想这样做的原因是要将错误报告到stdout(或stderr),所以不能关闭它们并在子进程中打开新文件。其次,如果任一open()调用返回错误,则进行fork将是浪费。


7

dup()最重要的一点是它返回一个可用于新文件描述符的最小整数。这是重定向的基础:

int fd_redirect_to = open("file", O_CREAT);
close(1); /* stdout */
int fd_to_redirect = dup(fd_redirect_to); /* magically returns 1: stdout */
close(fd_redirect_to); /* we don't need this */

在此之后,任何写入文件描述符1(标准输出)的内容都会神奇地进入“文件”中。


假设(不是毫无根据地)文件描述符0当前处于打开状态。如果没有dup2()(或dup3(),但这是系统的最近添加),支持诸如“200>somefile”之类的重定向非常不方便。 - Jonathan Leffler

4

Example:

close(1);     //closing stdout
newfd=dup(1); //newfd takes value of least available fd number

Where this happens to file descriptors:

0 stdin     .--------------.     0 stdin     .--------------.     0 stdin
1 stdout   =|   close(1)   :=>   2 stderr   =| newfd=dup(1) :=>   1 newfd
2 stderr    '--------------'                 '--------------'     2 stderr

A question arose again: How can I dup() a file descriptor that I already closed?

我怀疑你使用展示的结果进行以上实验,因为那不符合标准 - 参见dup

dup()函数会失败,如果:

[EBADF]
fildes参数不是有效的打开文件描述符。
所以,在展示代码序列之后,newfd必须不是1,而是-1,并且errnoEBADF

2
请查看这个页面,stdout可以被别名为dup(1)...

1

我有一个关于“复制标准输出”的提示。

在一些Unix系统上(但不包括GNU / Linux)

fd = open("/dev/fd/1", O_WRONLY);

it is equivalent to:

fd = dup(1);

0

dup()和dup2()系统调用

• dup()系统调用复制一个打开的文件描述符并返回新的文件描述符。

• 新的文件描述符与原始文件描述符具有以下共同属性: 1. 指向相同的打开文件或管道。 2. 具有相同的文件指针--也就是说,两个文件描述符共享一个文件指针。 3. 具有相同的访问模式,无论是读、写还是读写。

• dup()保证返回具有最低整数值的文件描述符。正是由于这个特性返回了最低未使用的文件描述符,使得进程可以完成I/O重定向。

int dup(file_descriptor)

int dup2(file_descriptor1, file_descriptor2)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接