进程间通信管道进阶篇:linux下dup/dup2函数的用法

由于利用管道实现进程间通信,是通过创建两个文件描述符,但是描述符的初始化是通过随机的,就是从可用的文件描述符中取出,并将可用的文件描述符与file对象相关联,如果我们需要将管道的两头与其他的流相关时,就需要重定向操作,重定向fd[0]和fd[1]的file,下面是关于实现重定向的函数dup和dup2的解释:

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

他的原型如下:

#include <unsitd.h>

int dup(int oldfd);

int dup2(int oldfd,int newfd);

dup 和dup2都是返回新的描述符。或者返回-1并设置 errno变量。新老描述符共享文件的偏移量(位置)、标志和锁,但是不共享close-on-exec标志。

 

相信大部分在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);  

       
        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可以给你带来很多强大的功能,花了一些时间总结出上面那么多,不知道自己理解的是否透彻,只能在以后的实践中慢慢探索了。

 

转载于:https://www.cnblogs.com/GODYCA/archive/2013/01/05/2846197.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/409257.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浙江大学计算机博士申请考核,考博经验|2020年浙江大学博士申请考核经验分享...

原标题&#xff1a;考博经验&#xff5c;2020年浙江大学博士申请考核经验分享本文系"研海拾珠"公众号原创&#xff0c;获取更多考博资料考博经验请移步公众号平台。作者 |Domin &#xff0c;浙江大学博士朝着目标坚定不移自我上学起&#xff0c;我爸妈就告诉我好好学…

python模拟购物车购物过程_Python 模拟购物车的实例讲解

1.功能简介此程序模拟用户登陆商城后购买商品操作。可实现用户登陆、商品购买、历史消费记查询、余额和消费信息更新等功能。首次登陆输入初始账户资金&#xff0c;后续登陆则从文件获取上次消费后的余额&#xff0c;每次购买商品后会扣除相应金额并更新余额信息&#xff0c;退…

西北大学计算机排行,世界排名领先,西北大学到底有多厉害?

原标题&#xff1a;世界排名领先&#xff0c;西北大学到底有多厉害&#xff1f;西北大学作为综排TOP9的美国名校&#xff0c;是许多留学生选择名校得最佳选择之一。那么作为世界排名领先的西北大学到底有多厉害&#xff1f;接下来立思辰留学云小编为你详细介绍。西北大学专业排…

Linux下的top命令

CSDN 阳光岛的文章&#xff1a; http://blog.csdn.net/sunboy_2050/article/details/6129177 最近使用了k命令杀死出bug的python进程&#xff08;cpu占用100%&#xff0c;ubuntu10的bug&#xff09;转载于:https://www.cnblogs.com/wolfsky/articles/2848702.html

是什么东西_隐形牙套附件是什么东西?

最近有一部分小宝贝收到牙套准备初佩戴啦&#xff01;但是有些小宝贝有疑惑了&#xff1a;粘在牙齿上的小凸点是什么东西&#xff1f;为什么要在牙齿上粘这个东西&#xff1f;微微细细询问后&#xff0c;原来让围观群众和正在矫正的同学们一脸懵b的就是——附件。什么是附件&am…

PHP header的一些用法

<?php //PHP header()函数用法 /*** Function: PHP header() examples (PHP) */// fix 404 pages: header(HTTP/1.1 200 OK);// set 404 header: header(HTTP/1.1 404 Not Found);// set Moved Permanently header (good for redrictions) // use with location header hea…

手游服务器验证,手游登录流程

接入第三方渠道后的手游登录流程一、客户端登录渠道流程&#xff1a;1.玩家从客户端输入user_name和password2.登录成功后&#xff0c;返回一个token传入&#xff1a;app_id、app_key、user_name、password返回&#xff1a;token说明&#xff1a;app_id、app_key是由游戏制作方…

区块链是大数据生态圈技术之一_区块链技术再发力,携手智能制造构建产业生态圈...

原标题&#xff1a;区块链技术再发力&#xff0c;携手智能制造构建产业生态圈大数据时代的逐渐逼近&#xff0c;使得传统行业面临巨大的挑战。区块链技术是大数据时代备受关注的新星&#xff0c;在越来越多的领域进行试点应用&#xff0c;区块链技术也是大数据时代日益强盛的关…

Array 复制到ArrayList中

string[] array new string[]{" 1", "2"," 3", "4"," 5" }; ArrayList list new ArrayList();一&#xff1a;使用for循环&#xff0c;将array数组中的数据逐步加入到ArrayList的对象中&#xff1b; //1、for循环 …

OpenCV学习笔记(1)——显示图片

最近开始学习OpenCV了&#xff0c;也打算通过写博客来记录学习opencv的笔记吧。 第一个程序就是比较简单&#xff0c;也是入门级的程序--加载图片并显示出来。 代码如下&#xff1a; #include"highgui.h"int main() {//从文件中提取图像IplImage * imgcvLoadImage(&q…

和包支付的钱哪里来_2019年支付宝年度账单出炉,来看看你究竟花了多少钱

今天早上&#xff0c;打开支付宝付款时&#xff0c;看见搜索框出现2019年账单时&#xff0c;该来的还是要要来。随后&#xff0c;小盾打开支付宝年度帐单看了起来&#xff0c;顿时就心中就出现了这样的疑问&#xff0c;我哪来的这么多钱&#xff1f;我怎么花了这么多钱&#xf…

从RGB到Lab色彩空间的转换

最近一直在学习绘制RGB,HSV,Lab色彩空间的直方图&#xff0c;其中也涉及到互相转换的知识&#xff0c;这是网上看到的介绍的。 虽然若干年前就看过了关于色彩空间的介绍&#xff0c;但是直到今天才自己动手写代码做这件事情。虽然网络上已经有很多现成的例子&#xff0c;但是一…

强制将IE8设置为IE7兼容模式来解析网页(转)

英文原文&#xff1a;http://msdn.microsoft.com/en-us/library/cc288325(VS.85).aspx 文件兼容性用于定义让IE如何编译你的网页。此文件解释文件兼容性&#xff0c;如何指定你网站的文件兼容性模式以及如何判断一个网页该使用的文件模式。 前言 为了帮助确保你的网页在所有未来…

css中怎么把数字改成罗马数字,$\LaTeX$笔记:Section 编号方式(数字、字母、罗马)计数器计数形式修改...

$\LaTeX$系列根目录&#xff1a; Latex学习笔记-序IEEE模板中Section的编号是罗马数字&#xff0c;要是改投其他刊物的话可能得用阿拉伯数字&#xff0c;所以可以在导言部分做如下修改(放在导言区宏包调用之后)&#xff1a;\renewcommand\thesection{\arabic{section}}%arabic …

creo动画如何拖动主体_Animate如何制作动态遮罩文字动画

使用遮罩可以制作文字动画&#xff0c;让文字变形图片填充并变化的动画效果。FLASH如何制作变色文字效果-百度经验​jingyan.baidu.comFLASH如何制作高光扫过文字的效果-百度经验​jingyan.baidu.com适用软件&#xff1a;Animate CC2018及其它FLash软件制作步骤&#xff1a;1.打…