dup/dup2函数学习

dup/dup2函数用来实现文件描述符之间的拷贝。对此,先来看看函数的声明:

#include <unistd.h>

int dup(int oldfd);
int dup2(int oldfd, int newfd);

dup函数

dup函数传入一个文件描述符,oldfd必须是已打开的文件描述符,否则dup函数调用失败。返回值为当前系统可用的最小文件描述符。测试程序如下:

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

int main()
{

    int fd = open("./data.txt", O_RDWR | O_CREAT);
    int newfd = 0;

    /* test oldfd argv */
    //close(fd);

    newfd = dup(fd);
    if (newfd < 0)
    {
        printf("dup error\n");
        return -1;
    }

    char buff[] = "Hello world";

    write(newfd, buff, sizeof(buff));
}

程序的运行结果为文件data.txt正确写入了"Hello world"。但是如果在dup之前先执行close(fd),那么dup函数调用是失败的。dup函数实现的功能类似fork函数之后父子进程共享文件描述符。可用下图(盗的图)来理解:

新旧描述符共享文件的偏移量(位置)、标志和锁,但是不共享close-on-exec标志。所以,都可以通过它们对文件进行操作。关于close-on-exec标志的解释:如果设置该文件描述符标志,那么通过fork函数产生的子进程中调用exec族函数,该文件描述符会被自动关闭。这样做可以用来保护文件描述符资源,防止其泄露。

dup2函数

dup2函数与dup函数起到相同的作用。不同的是dup2使用newfd来代替“当前最小可用的文件描述符”。那么与 dup类似,oldfd必须是已经打开的文件描述符,否则出错。关于newfd的说明:

  1. newfd可以仅仅是一个整数,而不是当前打开的文件描述符,这种情况下,dup2函数与dup函数类似,仅仅是使用newfd作为返回的文件描述符,而不是当前可用最小的文件描述符。
  2. newfd是当前已经打开的文件描述符,为了要对此文件描述符进行复用,dup2函数会先close该文件。相当于执行了close()-->dup()过程。不过需要注意的是,这个过程必须是原子操作。

note:

  1. 如果oldfd不是一个正确的正确的文件描述符,那么dup2调用失败,并且newfd不会被close。
  2. 如果oldfd是一个正确的文件描述符,并且newfd与oldfd具有相同的值,那么dup2函数什么都不做,只是返回newfd。

dup/dup2的使用

在单个进程中使用dup2函数可以起到重定向的作用。比如将标准输出重定向到指定的文件中。示例代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

int main()
{

    int fd = open("./data.txt", O_RDWR | O_CREAT);
    if (fd < 0)
    {
        printf("open error\n");
        printf("error is %s\n", strerror(errno));
        return -1;
    }

    int newfd = 0;

    newfd = dup2(fd, STDOUT_FILENO);
    if (newfd < 0)
    {
        printf("dup error\n");
        return -1;
    }

    printf("Hello world\n");
}

运行结果为通过调用printf函数可以直接将Hello world打印到文件中。可见dup2函数可以实现了重定向功能。为了在重定向之后恢复之前的文件描述符,通常使用下面的技巧:先保存后恢复,即先将STDOUT_FILENO通过dup函数保存起来,使用完成之后再利用dup2函数恢复。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

int main()
{

    char buff[] = "Hello world\n";
    int fd = open("./data.txt", O_RDWR | O_CREAT);
    if (fd < 0)
    {
        printf("open error\n");
        printf("error is %s\n", strerror(errno));
        return -1;
    }

    int newfd, savefd;

    // save
    savefd = dup(STDOUT_FILENO);
    if (savefd < 0)
    {
        printf("dup to savefd error\n");
        return -1;
    }

    newfd = dup2(fd, STDOUT_FILENO);
    if (newfd < 0)
    {
        printf("dup error\n");
        return -1;
    }

    write(STDOUT_FILENO, buff, sizeof(buff));

    // recovery
    if (dup2(savefd, STDOUT_FILENO) < 0)
    {
        printf("dup2 savefd error\n");
        return -1;
    }

    write(STDOUT_FILENO, buff, sizeof(buff));
}

需要注意的一个地方是,这里并没有使用标准库函数,而是使用了write系统调用。如果使用printf等函数,则最终结果为屏幕上输出两行"Hello world",而文件data.txt中为空,原因就是缓冲区作怪,由于最终的目标是屏幕,所以程序最后将缓冲区的内容都输出到屏幕。

上面的使用都是在一个进程内,现在这么考虑,如果涉及到两个进程,其中一个进程将标准输出重定向到某个文件描述符,第二个进程将此文件描述符重定向到自己的标准输出,那么这样的效果就是一个进程的输出在另一个进程的标准输出上显示出来。这就有点远程登录的意思了。试着来实现以下,为了问题的简单化,先在父子进程之间实现,其原因在于父子进程可以共享文件描述符。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

int main()
{

    int fd = open("./data.txt", O_RDWR | O_CREAT);
    int pid;
    if (fd < 0)
    {
        printf("open error\n");
        printf("error is %s\n", strerror(errno));
        return -1;
    }

    int newfd, savefd;
    if ((savefd = dup(STDOUT_FILENO)) < 0)
    {
        printf("dup savefd error\n");
        return -1;
    }

    newfd = dup2(fd, STDOUT_FILENO);
    if (newfd < 0)
    {
        printf("dup error\n");
        return -1;
    }

    printf("Hello world\n");
    if ((pid = fork()) == 0)
    {
        // child process
        // chang fd to stdout
        if (dup2(savefd, STDOUT_FILENO) < 0)
        {
            printf("dup2 savefd error\n");
            return -1;
        }
        if (dup2(STDOUT_FILENO, fd) < 0)
        {
            printf("child dup2 fd error\n");
            return -1;
        }
        printf("child hello world\n");
        sleep(2);
        close(fd);
        return;
    }
    else if (pid > 0)
    {
        // don‘t show in child stdout
        printf("parent hello world\n");
        close(fd);
    }
}

看上去代码的实现是对的,但是从运行的结果看却没有起到实际的效果。关于多进程之间使用dup/dup2函数还有待研究。

时间: 2024-10-27 00:22:12

dup/dup2函数学习的相关文章

笔记3-6: dup/dup2函数

dup与dup2函数 用于复制现存的文件描述符. 原型: #include <unistd.h> int dup(int fd); int dup2(int fd, int fd2); 两函数若成功则返回新描述符,出错则返回-1. dup函数返回一个新的描述符,并且这个新描述符一定是可用描述符中数值最小的一个. dup2函数使用fd2参数指定的数值返回新描述符,如果fd2已经打开,则先关闭fd2. 笔记3-6: dup/dup2函数,布布扣,bubuko.com

dup,dup2函数【转】

转自:http://eriol.iteye.com/blog/1180624 转自:http://www.cnblogs.com/jht/archive/2006/04/04/366086.html dup和dup2也是两个非常有用的调用,它们的作用都是用来复制一个文件的描述符.它们经常用来重定向进程的stdin.stdout和stderr.这两个函数的原形如下: C代码   #include <unistd.h> int dup( int oldfd ); int dup2( int old

dup/dup2函数

原子操作(automic operation)指的是由多步组成的操作.如果该操作原子的执行,则要么执行完所有步骤,要么一步也不执行,不可能只执行所有步骤的一个子集. 向打开文件时设置O_APPEND标志的文件写入是原子操作.内核每次对这种文件进行写之前,都将进程的当前偏移量设置到该文件的尾端处. O_EXCL | O_CREAT 是一个原子操作.如果文件已存在,则出错.否则,创建文件. dup/dup2两个函数都用于复制一个现存的文件描述符: #include<unistd.h> int du

linux下dup/dup2函数的用法

系统调用dup和dup2能够复制文件描述符.dup返回新的文件文件描述符(没有用的文件描述符最小的编号).dup2可以让用户指定返回的文件描述符的值,如果需要,则首先接近newfd的值,他通常用来重新打开或者重定向一个文件描述符. 他的原型如下: #include <unsitd.h> int dup(int oldfd); int dup2(int oldfd,int newfd); dup 和dup2都是返回新的描述符.或者返回-1并设置 errno变量.新老描述符共享文件的偏移量(位置)

linux之dup和dup2函数解析

1. 文件描述符在内核中数据结构在具体说dup/dup2之前,我认为有必要先了解一下文件描述符在内核中的形态.一个进程在此存在期间,会有一些文件被打开,从而会返回一些文件描述符,从shell中运行一个进程,默认会有3个文件描述符存在(0.1.2), 0与进程的标准输入相关联,1与进程的标准输出相关联,2与进程的标准错误输出相关联,一个进程当前有哪些打开的文件描述符可以通过/proc/进程ID/fd目录查看. 下图可以清楚的说明问题:进程表项 ---------------- fd标志 文件指针

dup和dup2函数

dup和dup2也是两个非常有用的调用,它们的作用都是用来复制一个文件的描述符.它们经常用来重定向进程的stdin.stdout和stderr.这两个函数的原形如下: #include <unistd.h> int dup( int oldfd ); int dup2( int oldfd, int targetfd ); dup()函数 利用函数dup,我们可以复制一个描述符.传给该函数一个既有的描述符,它就会返回一个新的描述符,这个新的描述符是传给它的描述符的拷贝.这意味着,这两个描述符共

Unix 网络编程 dup和dup2函数

dup和dup2也是两个非常有用的调用,它们的作用都是用来复制一个文件的描述符.它们经常用来重定向进程的stdin.stdout和stderr.这两个函数的原形如下: #include <unistd.h> int dup( int oldfd ); int dup2( int oldfd, int targetfd ); dup()函数: 利用函数dup,我们可以复制一个描述符.传给该函数一个既有的描述符,它就会返回一个新的描述符,这个新的描述符是传给它的描述符的拷贝.这意味着,这两个描述符

11Linux服务器编程之:VFS虚拟文件系统,dup()函数和dup2()函数

 1dup函数和dup2函数 #include<unistd.h> int dup(intoldfd); int dup2(intoldfd, int newfd); dup和dup2都可用来复制一个现存的文件描述符,使两个文件描述符指向同一个file结构体.如果两个文件描述符指向同一个file结构体,File Status Flag和读写位置只保存一份在file结构体中,并且file结构体的引用计数是2.如果两次open同一文件得到两个文件描述符,则每个描述符对应一个不同的file结构体

文件IO详解(十四)---dup函数和dup2函数详解

dup和dup2函数是在进程中用来复制文件描述符的,可以实现文件共享. ======================================================== 函数原型: 函数参数: oldfd:要被复制的文件描述符 newfd:在dup2函数中指定的新文件描述符 返回值: 调用成功返回新的文件描述符 调用失败返回 -1 ======================================================== 使用dup或者dup2函数实现文件共