zoukankan      html  css  js  c++  java
  • dup dup2与重定向(总结)

    dup和dup2函数

    #include <unistd.h>

    int dup(int filedes);

    int dup2(int filedes,int filedes2); //两函数的返回值:若成功则返回新的文件描述符,若出错则返回-1

     

    由dup返回的新文件描述符一定是当前可用文件描述符中的最小数值。用dup2则可以用filedes2参数指定新描述符的数值。

    如果filedes2已经打开,则先将其关闭。如果filedes等于filedes2,则dup2返回filedes2,则不关闭它。

     

    一、重定向问题:

    下面是一个例子程序:

    #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可以在父子进程之间建立一条“沟通的桥梁”。这里不详述。

     

    例子:

    dup2(fd,0);

    dup2(fd,1);

    dup2(fd,2);

    if(fd>2)

      close(fd);

     

    答:如果fd是1,执行dup2(fd,1)后返回1,但是没有关闭描述符1.调用3次dup2后,3个描述符指向相同的文件表项,所以不需要关闭描述符

       如果fd是3,调用3此dup2后,有4个描述符指向相同的文件表项,这时就需要关闭描述符3.

     

    重定向中注意:

    注意2>&1 >outfile 与 >outfile 2>&1  

      qun@ThinkPad ~/tmp $ ./a.out > outfile 2>&1

      qun@ThinkPad ~/tmp $ cat outfile

      output to stderr

      output to stdin

      qun@ThinkPad ~/tmp $ ./a.out 2>&1 > outfile

      output to stderr

      

    原因是:

      由于bash从左往右处理,在./a.out > outfile的时候,将a.out的fd 1定向到了outfile文件的fd上,然后才遇到2>&1,这时再将a.out的文件描述符2定向到文件描述符1上,这样stderr和stdout,就都到outfile上了。

    而下面一个则不然,先遇到2>&1,这时将a.out的文件描述符2定向到文件描述符1上,1是终端。再遇到 > outfile,这时将a.out的文件描述符1到outfile这个文件。

    用strace可以看到: 
    1. command > file 2>&1 
    这个命令中实现重定向的关键系统调用序列是: 
    open(file) == 3 
    dup2(3,1) 
    dup2(1,2) 

    2. command 2>&1 >file 
    这个命令中实现重定向的关键系统调用序列是: 
    dup2(1,2) 
    open(file) == 3 
    dup2(3,1) 

                                                                                   

                                                                                                                                                         ————————————收集总结

  • 相关阅读:
    经典基础算法之面试题(系列一)
    Shell脚本中的二维字符串列表
    网络字节流数据解析组件的设计与实现Circular Buffer(Ring Buffer)
    Django开发环境搭建(Windows+Python2.6)
    C++中堆(优先队列)的应用:make_heap, pop_heap, push_heap, sort_heap, priority_queue
    求逆序数的快速算法归并排序
    经典基础算法之BST详解(系列二)
    PHP数组去重
    微信小程序分享设置
    微信小程序判断开发环境
  • 原文地址:https://www.cnblogs.com/moonflow/p/2461676.html
Copyright © 2011-2022 走看看