转自:http://eriol.iteye.com/blog/1180624

转自:http://www.cnblogs.com/jht/archive/2006/04/04/366086.html

dup和dup2也是两个非常有用的调用,它们的作用都是用来复制一个文件的描述符。它们经常用来重定向进程的stdin、stdout和stderr。这两个函数的原形如下:

  1. #include <unistd.h>
  2. int dup( int oldfd );
  3. int dup2( int oldfd, int targetfd );

dup()函数

利用函数dup,我们可以复制一个描述符。传给该函数一个既有的描述符,它就会返回一个新的描述符,这个新的描述符是传给它的描述符的拷贝。这意味着,这两个描述符共享同一个数据结构。例如,如果我们对一个文件描述符执行lseek操作,得到的第一个文件的位置和第二个是一样的。下面是用来说明dup函数使用方法的代码片段:

  1. int fd1, fd2;
  2. ...
  3. fd2 = dup( fd1 );

需要注意的是,我们可以在调用fork之前建立一个描述符,这与调用dup建立描述符的效果是一样的,子进程也同样会收到一个复制出来的描述符。

dup2()函数

dup2函数跟dup函数相似,但dup2函数允许调用者规定一个有效描述符和目标描述符的id。dup2函数成功返回时,目标描述符(dup2函数的第二个参数)将变成源描述符(dup2函数的第一个参数)的复制品,换句话说,两个文件描述符现在都指向同一个文件,并且是函数第一个参数指向的文件。下面我们用一段代码加以说明:

  1. int oldfd;
  2. oldfd = open("app_log", (O_RDWR | O_CREATE), 0644 );
  3. dup2( oldfd, 1 );
  4. close( oldfd );

在本例中,我们打开了一个新文件,称为“app_log”,并收到一个文件描述符,该描述符叫做fd1。我们调用dup2函数,参数为oldfd和1,这会导致用我们新打开的文件描述符替换掉由1代表的文件描述符(即stdout,因为标准输出文件的id为1)。任何写到stdout的东西,现在都将改为写入名为“app_log”的文件中。需要注意的是,dup2函数在复制了oldfd之后,会立即将其关闭,但不会关掉新近打开的文件描述符,因为文件描述符1现在也指向它。

例子

下面我们介绍一个更加深入的示例代码。回忆一下命令行管道,我们可以将ls –l命令的标准输出作为标准输入连接到wc –l命令。接下来,我们就用一个C程序来加以说明这个过程的实现。代码如下所示。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. int main()
  5. {
  6. int pfds[2];
  7. if ( pipe(pfds) == 0 ) {
  8. if ( fork() == 0 ) {
  9. close(1);
  10. dup2( pfds[1], 1 );
  11. close( pfds[0] );
  12. execlp( "ls", "ls", "-l", NULL );
  13. } else {
  14. close(0);
  15. dup2( pfds[0], 0 );
  16. close( pfds[1] );
  17. execlp( "wc", "wc", "-l", NULL );
  18. }
  19. return 0;
  20. }

在示例代码中,首先在第9行代码中建立一个管道,然后将应用程序分成两个进程:一个子进程(第13–16行)和一个父进程(第20–23行)。接下来,在子进程中首先关闭stdout描述符(第13行),然后提供了ls –l命令功能,不过它不是写到stdout(第13行),而是写到我们建立的管道的输出端,这是通过dup2函数来完成重定向的。在第14行,使用dup2 函数把stdout重定向到管道(pfds[1])。之后,马上关掉管道的输入端。然后,使用execlp函数把子进程的映像替换为命令ls –l的进程映像,一旦该命令执行,它的任何输出都将发给管道的输入端。

现在来研究一下管道的接收端。从代码中可以看出,管道的接收端是由父进程来担当的。首先关闭stdin描述符(第20行),因为我们不会从机器的键盘等标准设备文件来接收数据的输入,而是从其它程序的输出中接收数据。然后,再一次用到dup2函数(第21行),让管道的输入端作为输入,这是通过让文件描述符0(即常规的stdin)重定向到pfds[0]实现的。关闭管道的stdout端(pfds[1]),因为在这里用不到它。最后,使用 execlp函数把父进程的映像替换为命令wc -l的进程映像,命令wc -l把管道的内容作为它的输入(第23行)。

这两个函数的功能是输出的重定向
      定义这两个函数的头文件是unistd.h,有兴趣的可以自己看看这个头文件包含的内容
      
      要提的是这个头文件同时定义了下面三个常量

兄弟们学习网络编程用0,1,2这些参数的时候也得知道代表的意思

要说这两个函数的意思,还是看一段具体的代码

int fd, fd2;  
mode_t fd_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;  
 
void redir_stdout(const char *filename)  
{  
    fd2=dup(STDOUT_FILENO);  
    fd = open(filename, O_WRONLY|O_CREAT, fd_mode);  //打开文件操作
    dup2(fd, STDOUT_FILENO);  //把输出重定向到fd标识的文件
    close(fd);  
}  

fd2=dup(STDOUT_FILENO);说明fd2表示了标准输出
      如果我们想把刚刚定向到fd的输出,再定向回标准输出,我们可以用下面的代码实现:

void resume_stdout()  //恢复输出,把标准输出定向到fd2,fd2代表的是标准输出
{  
    dup2(fd2, STDOUT_FILENO);   
    close(fd2);  

dup,dup2函数【转】的更多相关文章

  1. linux下dup/dup2函数的用法

    系统调用dup和dup2能够复制文件描述符.dup返回新的文件文件描述符(没有用的文件描述符最小的编号).dup2可以让用户指定返回的文件描述符的值,如果需要,则首先接近newfd的值,他通常用来重新 ...

  2. linux之dup和dup2函数解析

    1. 文件描述符在内核中数据结构在具体说dup/dup2之前,我认为有必要先了解一下文件描述符在内核中的形态.一个进程在此存在期间,会有一些文件被打开,从而会返回一些文件描述符,从shell中运行一个 ...

  3. dup和dup2应用实例(dup跟APUE有出入,close+dup=dup2?)

    dup/dup2函数 有时我们希望把标准输入重定向到一个文件,或者把标准输出重定向到一个网络连接. dup()与dup2()能对输入文件描述符进行重定向. 函数原型如下: dup函数创建一个新的文件描 ...

  4. dup和dup2函数

    下面两个函数都可用来复制一个现存的文件描述符: #include<unistd.h> int dup(int filedes); int dup2(int filedes,int file ...

  5. dup和dup2函数以及管道的实现

    疑问:管道应该不是这样实现的,因为这要求修改程序的代码 dup和dup2也是两个非常有用的调用,它们的作用都是用来复制一个文件的描述符.它们经常用来重定向进程的stdin.stdout和stderr. ...

  6. Unix 网络编程 dup和dup2函数

    dup和dup2也是两个很实用的调用,它们的作用都是用来复制一个文件的描写叙述符. 它们经经常使用来重定向进程的stdin.stdout和stderr.这两个函数的原形例如以下: #include & ...

  7. 【APUE】第3章 文件I/O (3) 文件共享、原子操作、函数dup/dup2、函数sync/fsync/fdatasync、函数fcntl、函数ioct1、目录/dev/fd 使用说明

    1.文件共享 UNIX系统支持在不同的进程间共享打开文件.为了说明这种共享,以下介绍内核用于所有I/O的数据结构. 内核使用3种数据结构表示打开文件,它们之间的关系决定了在文件共享方面一个进程对另一个 ...

  8. dup和dup2函数简单使用

    dup函数 头文件和函数原型: #include <unistd.h> int dup(int oldfd); dup函数是用来打开一个新的文件描述符,指向和oldfd同一个文件,共享文件 ...

  9. [APUE]不用fcntl实现dup2函数功能

    dup2的函数定义为: #include <unistd.h> int dup2(int src_fd, int new_fd); 自己实现dup2函数有几个关键点: 1,检查给定的源fd ...

随机推荐

  1. 添加AD RMS role时,提示密码不能被验证The password could not be validated

    "The password could not be validated" when attempting to provision an AD RMS server. Sympt ...

  2. 用DotNetOpenAuth实现基于OAuth 2.0的web api授权 (一)Getting Start

    1. 下载 源码下载 2. build solution,创建虚拟目录: 右健MyContatacts/MyPromo项目,选择Properties,点击左边的Web,点击 Create Virtua ...

  3. A1073. Scientific Notation

    Scientific notation is the way that scientists easily handle very large numbers or very small number ...

  4. [noip][2017]

    Day1T1 30分思路: 倒着枚举答案z,用扩展欧几里得求解,如果能找到两个非负整数x,y使得ax+by=z则继续枚举,直到无解为止 100分: 最适用与考场上的做法,根据30分思路打表找规律. 3 ...

  5. 惊喜的gift

    情侣的饰品吧!我之前在淘宝上买了一套,挺好的,很好看呢!男的手链,女的项链,手链需要项链的钥匙才能打得开,女孩子应该都会很喜欢的吧!至少我很喜欢!而且价格也不贵,150块人民币都不到,很适合当生日礼物 ...

  6. DOM表格操作

    注意:就算代码中不包含<tbody>标签,浏览器解析时也可能会自动添加,因此需要注意子元素的选择 表格操作用到的属性: 1.tHead 2.tBodies 3.tFoot 更为细致的有: ...

  7. Educational Codeforces Round 42 (Rated for Div. 2) D. Merge Equals

    http://codeforces.com/contest/962/problem/D D. Merge Equals time limit per test 2 seconds memory lim ...

  8. vue2.0 之事件处理器

    事件绑定v-on(内置事件) <template> <div> <a v-if="isPartA">partA</a> <a ...

  9. ubuntu14.04上java jdk & mvn安装

    这些常用工具的安装步骤还是自己记录下,以后再次用到时就会方便许多. 系统:ubuntu14.04 jdk安装. 1.从官网下载好jdk安装包 jdk-8u111-linux-x64.tar.gz 2. ...

  10. GO语言的进阶之路-go的程序结构以及包简介

    GO语言的进阶之路-go的程序结构以及包简介 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.编辑,编译和运行 A,编辑 Go程序使用UTF-8编码的纯Unicode文本编写.大 ...