【C】——dup/dup2用法
相信大部分在Unix/Linux下编程的程序员手头上都有《Unix环境高级编程》(APUE)这本超级经典巨著。作者在该书中讲解dup/dup2之前曾经讲过“文件共享”,这对理解dup/dup2还是很有帮助的。这里做简单摘录以备在后面的分析中使用:
Stevens said:
(1) 每个进程在进程表中都有一个记录项,每个记录项中有一张打开文件描述符表,可将视为一个矢量,每个描述符占用一项。与每个文件描述符相关联的是:
   (a) 文件描述符标志。
   (b) 指向一个文件表项的指针。
(2) 内核为所有打开文件维持一张文件表。每个文件表项包含:
   (a) 文件状态标志(读、写、增写、同步、非阻塞等)。
   (b) 当前文件位移量。
   (c) 指向该文件v节点表项的指针。
图示:
   文件描述符表
   ------------
fd0  0   | p0  -------------> 文件表0 ---------> vnode0
   ------------
fd1  1   | p1  -------------> 文件表1 ---------> vnode1
   ------------
fd2  2   | p2  
   ------------
fd3  3   | p3  
   ------------
... ...
... ...
   ------------
一、单个进程内的dup和dup2
假设进程A拥有一个已打开的文件描述符fd3,它的状态如下:
  进程A的文件描述符表(before dup2)
   ------------
fd0  0   | p0  
   ------------
fd1  1   | p1  -------------> 文件表1 ---------> vnode1
   ------------
fd2  2   | p2  
   ------------
fd3  3   | p3  -------------> 文件表2 ---------> vnode2
   ------------
... ...
... ...
   ------------
经下面调用:
n_fd = dup2(fd3, STDOUT_FILENO);后进程状态如下:
进程A的文件描述符表(after dup2)
   ------------
fd0  0   | p0  
   ------------
n_fd 1   | p1  ------------
   ------------               \
fd2  2   | p2                 \
   ------------                 _\|
fd3  3   | p3  -------------> 文件表2 ---------> vnode2
   ------------
... ...
... ...
   ------------
解释如下:
n_fd = dup2(fd3, STDOUT_FILENO)表示n_fd与fd3共享一个文件表项(它们的文件表指针指向同一个文件表项),n_fd在文件描述符表中的位置为 STDOUT_FILENO的位置,而原先的STDOUT_FILENO所指向的文件表项被关闭,我觉得上图应该很清晰的反映出这点。按照上面的解释我们 就可以解释CU中提出的一些问题:
(1) "dup2的第一个参数是不是必须为已打开的合法filedes?" -- 答案:必须。
(2) "dup2的第二个参数可以是任意合法范围的filedes值么?" -- 答案:可以,在Unix其取值区间为[0,255]。
另外感觉理解dup2的一个好方法就是把fd看成一个结构体类型,就如上面图形中画的那样,我们不妨把之定义为:
struct fd_t {
 int index;
 filelistitem *ptr;
};
然后dup2匹配index,修改ptr,完成dup2操作。
在学习dup2时总是碰到“重定向”一词,上图完成的就是一个“从标准输出到文件的重定向”,经过dup2后进程A的任何目标为STDOUT_FILENO的I/O操作如printf等,其数据都将流入fd3所对应的文件中。下面是一个例子程序:
#define TESTSTR "Hello dup2\n"
int main() {
        int     fd3;
fd3 = open("testdup2.dat", 0666);
        if (fd < 0) {
                printf("open error\n");
                exit(-1);
        }
if (dup2(fd3, STDOUT_FILENO) < 0) {        
                printf("err in dup2\n");
        }
        printf(TESTSTR);
        return 0;
}
其结果就是你在testdup2.dat中看到"Hello dup2"。
二、重定向后恢复
CU上有这样一个帖子,就是如何在重定向后再恢复原来的状态?首先大家都能想到要保存重定向前的文件描述符。那么如何来保存呢,象下面这样行么?
int s_fd = STDOUT_FILENO;
int n_fd = dup2(fd3, STDOUT_FILENO);
还是这样可以呢?
int s_fd = dup(STDOUT_FILENO);
int n_fd = dup2(fd3, STDOUT_FILENO);
这 两种方法的区别到底在哪呢?答案是第二种方案才是正确的,分析如下:按照第一种方法,我们仅仅在"表面上"保存了相当于fd_t(按照我前面说的理解方 法)中的index,而在调用dup2之后,ptr所指向的文件表项由于计数值已为零而被关闭了,我们如果再调用dup2(s_fd, fd3)就会出错(出错原因上面有解释)。而第二种方法我们首先做一下复制,复制后的状态如下图所示:
进程A的文件描述符表(after dup)
   ------------
fd0  0   | p0  
   ------------
fd1  1   | p1  -------------> 文件表1 ---------> vnode1
   ------------                 /|
fd2  2   | p2               /
   ------------             /
fd3  3   | p3  -------------> 文件表2 ---------> vnode2
   ------------          /
s_fd 4   | p4  ------/  
   ------------
... ...
... ...
   ------------
调用dup2后状态为:
进程A的文件描述符表(after dup2)
   ------------
fd0  0   | p0  
   ------------
n_fd 1   | p1  ------------
   ------------               \
fd2  2   | p2                \
   ------------                _\|
fd3  3   | p3  -------------> 文件表2 ---------> vnode2
   ------------
s_fd 4   | p4  ------------->文件表1 ---------> vnode1 
   ------------
... ...
... ...
   ------------
dup(fd)的语意是返回的新的文件描述符与fd共享一个文件表项。就如after dup图中的s_fd和fd1共享文件表1一样。
确定第二个方案后重定向后的恢复就很容易了,只需调用dup2(s_fd, n_fd);即可。下面是一个完整的例子程序:
#define TESTSTR "Hello dup2\n"
#define SIZEOFTESTSTR 11
int main() {
        int     fd3;
        int     s_fd;
        int     n_fd;
fd3 = open("testdup2.dat", 0666);
        if (fd3 < 0) {
                printf("open error\n");
                exit(-1);
        }
/* 复制标准输出描述符 */
        s_fd = dup(STDOUT_FILENO);
        if (s_fd < 0) {
                printf("err in dup\n");
        }
/* 重定向标准输出到文件 */
        n_fd = dup2(fd3, STDOUT_FILENO);
        if (n_fd < 0) {
                printf("err in dup2\n");
        }
        write(STDOUT_FILENO, TESTSTR, SIZEOFTESTSTR);   /* 写入testdup2.dat中 */
/* 重定向恢复标准输出 */
        if (dup2(s_fd, n_fd) < 0) {
                printf("err in dup2\n");
        }
        write(STDOUT_FILENO, TESTSTR, SIZEOFTESTSTR); /* 输出到屏幕上 */
        return 0;
}
注 意这里我在输出数据的时候我是用了不带缓冲的write库函数,如果使用带缓冲区的printf,则最终结果为屏幕上输出两行"Hello dup2",而文件testdup2.dat中为空,原因就是缓冲区作怪,由于最终的目标是屏幕,所以程序最后将缓冲区的内容都输出到屏幕。
三、父子进程间的dup/dup2
由fork调用得到的子进程和父进程的相同文件描述符共享同一文件表项,如下图所示:
父进程A的文件描述符表
   ------------
fd0  0   | p0  
   ------------
fd1  1   | p1  -------------> 文件表1 ---------> vnode1
   ------------                            /|\
fd2  2   | p2                            |
   ------------                             |
                                              |
子进程B的文件描述符表                |
   ------------                             |
fd0  0   | p0                            |
   ------------                             |
fd1  1   | p1  ---------------------|
   ------------
fd2  2   | p2  
   ------------
所以恰当的利用dup2和dup可以在父子进程之间建立一条“沟通的桥梁”。这里不详述。
四、小结
灵活的利用dup/dup2可以给你带来很多强大的功能,花了一些时间总结出上面那么多,不知道自己理解的是否透彻,只能在以后的实践中慢慢探索了。
【C】——dup/dup2用法的更多相关文章
- linux下dup/dup2函数的用法
		
系统调用dup和dup2能够复制文件描述符.dup返回新的文件文件描述符(没有用的文件描述符最小的编号).dup2可以让用户指定返回的文件描述符的值,如果需要,则首先接近newfd的值,他通常用来重新 ...
 - dup和dup2应用实例(dup跟APUE有出入,close+dup=dup2?)
		
dup/dup2函数 有时我们希望把标准输入重定向到一个文件,或者把标准输出重定向到一个网络连接. dup()与dup2()能对输入文件描述符进行重定向. 函数原型如下: dup函数创建一个新的文件描 ...
 - 文件操作篇 close creat dup dup2 fcntl flock fsync lseek mkstemp open read sync write
		
文件操作篇 close creat dup dup2 fcntl flock fsync lseek mkstemp open read sync write close(关闭文件) 相关函数 ope ...
 - 【APUE】第3章 文件I/O (3) 文件共享、原子操作、函数dup/dup2、函数sync/fsync/fdatasync、函数fcntl、函数ioct1、目录/dev/fd 使用说明
		
1.文件共享 UNIX系统支持在不同的进程间共享打开文件.为了说明这种共享,以下介绍内核用于所有I/O的数据结构. 内核使用3种数据结构表示打开文件,它们之间的关系决定了在文件共享方面一个进程对另一个 ...
 - dup和dup2用法小结
		
今天和同学探讨了一下关于重定向输出到文件的问题,其中需要用到dup和dup2函数,因此来小小的总结一下. 首先来man一下: dup直接返回一个新的描述符和原来的描述符一样代表同一个资源,描述符的值就 ...
 - dup,dup2函数【转】
		
转自:http://eriol.iteye.com/blog/1180624 转自:http://www.cnblogs.com/jht/archive/2006/04/04/366086.html ...
 - linux系统编程之文件与IO(八):文件描述符相关操作-dup,dup2,fcntl
		
本节目标: 1,文件共享 打开文件内核数据结构 一个进程两次打开同一个文件 两个进程打开同一个文件 2,复制文件描述符(dup.dup2.fcntl) 一,文件共享 1,一个进程打开两个文件内核数据结 ...
 - VFS dup ,dup2
		
Linux支持各种各样的文件系统格式,如ext2.ext3.reiserfs.FAT.NTFS.iso9660等等,不同的磁盘分区.光盘或其它存储设备都有不同的文件系统格式,然而这些文件系统都可以mo ...
 - Linux系统编程--文件描述符的复制dup()和dup2()【转】
		
本文转载自:http://blog.csdn.net/tennysonsky/article/details/45870459 dup() 和 dup2() 是两个非常有用的系统调用,都是用来复制一个 ...
 
随机推荐
- SpringBoot自定义序列化的使用方式--WebMvcConfigurationSupport
			
场景及需求: 项目接入了SpringBoot开发,现在需求是服务端接口返回的字段如果为空,那么自动转为空字符串. 例如: [ { "id": 1, ...
 - 如何打包和生成你的Android应用程序
			
原文:http://android.eoe.cn/topic/android_sdk 在生成过程中,你的Android项目的编译和打包成一个apk文件,为您的应用程序二进制的容器.它包含了所有必要的信 ...
 - 防CSRF攻击:一场由重复提交的问题引发的前端后端测试口水战
			
重复提交,这是一直以来都会存在的问题,当在网站某个接口调用缓慢的时候就会有可能引起表单重复提交的问题,不论form提交,还是ajax提交都会有这样的问题,最近在某社交app上看到这么一幕,这个团队没有 ...
 - Atitit 软件项目系统托盘图标解决方案
			
Atitit 软件项目系统托盘图标解决方案 1.1. Nodejs node-webkit还实现了本地化的API,例如菜单栏,系统的托盘图标支持1 1.2. Java c# qt c++1 1.3 ...
 - 【Unity】4.6 灯光
			
分类:Unity.C#.VS2015 创建日期:2016-04-11 一.简介 灯光(Light,也叫光源)是每一个场景的重要组成部分,用于照亮场景和对象,从而让游戏具有自己的个性和风格,比如利用灯光 ...
 - K8s 介绍
			
Kubernetes(k8s)是自动化容器操作的开源平台,这些操作包括部署,调度和节点集群间扩展. 使用Kubernetes可以: 1. 自动化容器的部署和复制 2. 随时扩展或收缩容器规模 3. 将 ...
 - 菜鸟学SSH(九)——Hibernate——Session之save()方法
			
Session的save()方法用来将一个临时对象转变为持久化对象,也就是将一个新的实体保存到数据库中.通过save()将持久化对象保存到数据库需要经过以下步骤: 1,系统根据指定的ID生成策略,为临 ...
 - VLAN,GRE,VXLAN
			
https://www.cnblogs.com/charles1ee/p/6629009.html VLAN介绍 局域网LAN(多个机器连接一个Hub或者Switch),是一个广播域 VLAN:一台S ...
 - linux命令(40):基础常用命令:cd,rm,mk
			
常用命令介绍 pwd,显示当前在哪个路径下 linux的用户管理 : useradd 用户名,添加用户 [案例]useradd xiaoming pas ...
 - iOS开发-通讯录有界面
			
// // ViewController.m // 06-通讯录(有界面) // #import "ViewController.h" #import <AddressB ...