实际示例使用dup或dup2。

73
我知道dup / dup2的作用,但我不知道它什么时候会被使用。
有没有实际的例子?

5个回答

75

一个例子是I/O重定向。为此,您要fork一个子进程并关闭stdin或stdout文件描述符(0和1),然后对另一个您选择的文件描述符执行dup()操作,该文件描述符现在将映射到最低可用文件描述符,这种情况下是0或1。

使用这个技术,您可以执行任何可能不了解您的应用程序的子进程,当子进程在stdout上写入数据(或从stdin读取数据,无论您如何配置),数据将被写入提供的文件描述符。

Shell使用这种方法来实现命令行管道,例如/bin/ls | more将一个进程的stdout连接到另一个进程的stdin。


3
您可以使用常规的 dup() 函数将标准输出复制到另一个文件描述符,然后再将其重定向。这样,您就可以稍后取消重定向。如果您使用了 dup2() 函数,则有可能会覆盖现有的文件描述符。 - Kevin
@krusty:请参考这个答案,它是针对另一个S.O.问题的。 - user1071847

33

了解dup和dup2最好的场景是重定向。
首先我们需要知道的是,系统有三个默认文件id(或指示输入或输出源的变量)来处理输入和输出。它们是stdinstdoutstderr,以整数表示分别为012。大多数函数(如fprintfcout)直接输出到stdout
如果我们想要重定向输出,一种方法是给fprintf函数更多的参数来指示inout
然而,有一种更简单优雅的方法:我们可以重写默认文件id,使它们指向我们想要接收输出的文件。这就是dupdup2所起作用的情况。
现在让我们从一个简单的例子开始:假设我们想将fprintf的输出重定向到名为"chinaisbetter.txt"的txt文件中。首先,我们需要打开这个文件。

int fw=open("chinaisbetter.txt", O_APPEND|O_WRONLY);

然后我们想使用dup函数将stdout指向"chinaisbetter.txt":

dup2(fw,1);

现在,尽管标准输出仍为1,但stdout(1)指向“chinaisbetter.txt”的描述符,因此输出已被重定向。
然后,您可以像正常情况下使用printf,但结果将写入txt文件中,而不是直接显示在屏幕上:

printf("Are you kidding me? \n");

PS:

  1. 这只是一个直观的解释,您可能需要查看manpage或详细信息。实际上,我们在这里说“复制”,它们并没有复制所有内容。

  2. 此处的文件ID是指文件的句柄。上面提到的文件描述符是记录文件信息的结构体。


3
这个答案需要一个好的s/fprintf/printf/gfprintf将内容写入明确命名的文件描述符,而printf将内容写入stdout - Mahmoud Al-Qudsi

10
当你对POSIX函数,特别是那些看起来重复的函数感到好奇时,通常最好查看标准本身。在底部通常会看到示例,以及实现(和存在)两者的原因。
在这种情况下:
以下章节为信息性章节。
示例
将标准输出重定向到文件
以下示例关闭当前进程的标准输出,重新分配标准输出以转到pfd引用的文件,并关闭原始文件描述符以进行清理。
#include <unistd.h>
...
int pfd;
...
close(1);
dup(pfd);
close(pfd);
...

重定向错误消息

以下示例将来自stderr的消息重定向到stdout

#include <unistd.h>
...
dup2(2, 1); // 2-stderr; 1-stdout
...

应用使用

无。

原理

dup()dup2()函数是多余的。它们的服务也由fcntl()函数提供。它们被包含在IEEE Std 1003.1-2001的本卷中,主要是出于历史原因,因为许多现有应用程序使用它们。

虽然所示的简短代码段与dup2()的行为非常相似,但基于IEEE Std 1003.1-2001本卷中定义的其他函数的符合实现要复杂得多。最不明显的是可能会在步骤之间调用信号捕获函数并分配或释放文件描述符的可能影响。这可以通过阻止信号来避免。

dup2()函数没有标记为过时,因为它提供了一个类型安全的功能版本,由fcntl()提供了一个类型不安全的版本。它用于POSIX Ada绑定。

dup2()函数不适用于关键区域作为同步机制。

在[EBADF]的描述中,fildes超出范围的情况已经包括在给定的fildes无效的情况下。fildes和fildes2的描述不同,因为对于fildes2相关的无效性,是否它超出范围是唯一相关的;也就是说,在进行dup2()调用时,fildes2是否引用打开的文件并不重要。

未来方向

无。

参见

close(), fcntl(), open(), IEEE Std 1003.1-2001的基本定义卷,<unistd.h>

变更历史

首次发布于Issue 1。源自SVID的Issue 1。


重定向的定义是什么?你写入fd1的所有内容也将被写入fd2。 - Bionix1441
3
这并没有帮上忙,你说 RTFM 的话也一样。 - Andrew

5

一个实际的例子是将输出信息重定向到其他流,比如某个日志文件。以下是一个I/O重定向的示例代码。
请参考原始帖子这里

#include <stdio.h>

main()
{
    int    fd;
    fpos_t pos;

    printf("stdout, ");

    fflush(stdout);
    fgetpos(stdout, &pos);
    fd = dup(fileno(stdout));
    freopen("stdout.out", "w", stdout);

    f();

    fflush(stdout);
    dup2(fd, fileno(stdout));
    close(fd);
    clearerr(stdout);
    fsetpos(stdout, &pos);        /* for C9X */

    printf("stdout again\n");
}

f()
{
printf("stdout in f()");
}

4

在shell中进行I/O重定向最有可能使用dup2/fcntl系统调用来实现。

我们可以使用dup2函数轻松模拟$program 2>&1 > logfile.log这种类型的重定向。

下面的程序重定向了stdout和stderr,即使用dup2模拟了$program 2>&1 > output的行为。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int
main(void){
    int close_this_fd;
    dup2(close_this_fd = open("output", O_WRONLY), 1);
    dup2(1,2);
    close(close_this_fd);
    fprintf(stdout, "standard output\n");
    fprintf(stderr, "standard error\n");
    fflush(stdout);
    sleep(100); //sleep to examine the filedes in /proc/pid/fd level.
    return;
}

vagrant@precise64:/vagrant/advC$ ./a.out
^Z
[2]+  Stopped                 ./a.out
vagrant@precise64:/vagrant/advC$ cat output
standard error
standard output
vagrant@precise64:/vagrant/advC$ ll /proc/2761/fd
total 0
dr-x------ 2 vagrant vagrant  0 Jun 20 22:07 ./
dr-xr-xr-x 8 vagrant vagrant  0 Jun 20 22:07 ../
lrwx------ 1 vagrant vagrant 64 Jun 20 22:07 0 -> /dev/pts/0
l-wx------ 1 vagrant vagrant 64 Jun 20 22:07 1 -> /vagrant/advC/output
l-wx------ 1 vagrant vagrant 64 Jun 20 22:07 2 -> /vagrant/advC/output

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