深入dup2的内部细节

本文详细探讨了dup2系统调用的工作原理,包括文件描述符表、文件表和v节点表等核心概念。通过理解这些,我们可以明白dup2如何使两个文件描述符指向同一文件表项,实现共享文件状态。此外,文章还解答了关于dup2使用时的一些关键问题,如源文件描述符是否需要打开,目标文件描述符是否需要关闭等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基础信息:

  1. 所需头文件:#include <unistd.h>
  2. 函数原型:int dup2(int oldfd, int newfd);
  3. 返回值:成功返回新的文件描述符,失败返回-1
  4. 功能:将oldfd描述符复制给newfd

深入理解:

当我们把oldfdnewfd传入到dup2时,到底发生了什么呢?
这里需要用到有关文件方面的知识了,作一个简要的说明(具体请参考我之前的博客http://blog.youkuaiyun.com/move_now/article/details/62054836):
内核在表示一个打开的文件时使用了三个数据结构,文件描述符表,文件表以及v节点表
1. 文件描述符表:每个进程都独有的一张用于存储文件描述符标志和指向文件表某一项的文件指针
2. 文件表:每一项都代表了一个打开的文件,存储的是文件的状态标志以及当前文件的偏移量还有指向v节点表的指针(注意同一文件可以被打开多次)
3. v节点表:存储了v节点的信息(文件类型以及对此文件进行操作的各种函数的指针)和i节点。i节点是在打开文件时从磁盘上读入内存的,里面存储了文件的大量信息,我们平时使用的ls -l命令读取的信息大部分就是从i节点里面提取出来的。

知道了这些,其实我们调用dup2之后,相当于newfd的文件指针被oldfd的文件指针覆盖了,这样造成的结果就是两者指向同一个文件表项。于是它们共享同一文件的状态标志、偏移量还有指向相同的v节点表以及各种该文件的信息。这就是为什么调用了dup2之后,两个文件描述符都可以用来访问同一个文件的原因。

细节部分:

接下来有几个问题,如果弄懂了那证明就已经理解了:
问题一:当我们使用dup2时,oldfd是否需要处于打开状态呢?
答案是需要,因为只有打开的文件才会在文件表中占有一项,它的文件指针才有作用,这样赋给newfd才有意义。
问题二:newfd是否需要处于打开状态呢?
回答是不需要,就算打开了,也会先被关闭(除非newfdoldfd是相同的文件描述符)。dup2完成的功能相当于是先调用close关闭newfd,然后再调用fcntl(oldfd, F_DUPFD, newfd),不过dup2是原子操作。
问题三:既然我们传入处于打开状态的newfd会被先关闭掉,那么调用了dup2之后如果再打开一个新的文件,那么newfd是否会被用于打开的新的文件的描述符呢?
结果是不会,newfd索引到的文件描述符表非空,是处于被占用状态的,所以不会分配给新的文件。
这里为了加深印象,给出一段测试代码:

#include <iostream>
using namespace std;
#include <unistd.h>
#include <fcntl.h>
#include <string>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    char s[] = "123123";
    int fd = open("test.txt", O_RDWR | O_CREAT, 0644);
    cout << "fd :" << fd << endl;
    //fd会先被close掉,不过是在dup2内部完成的
    dup2(STDOUT_FILENO, fd);
    //这里打开了一个新的文件,但是输出发现占用的是fd之后的文件描述符
    int fd1 = open("test1.txt", O_RDWR | O_CREAT, 0644);
    cout << "fd1:" << fd1 << endl;
    if(write(fd, s, sizeof(s)) < 0)
    {
        perror("write error:");
        exit(1);
    }
    close(fd);
    close(fd1);
    return 0;
}

题外话:

之前提到了一下文件描述符标志文件的状态标志。这两者的不同之处其实根据所存储它们的数据结构就可以得到这样的结论:文件描述符标志是仅仅针对文件描述符的,当用另外的文件描述符索引到同一个文件时,该标志在新的文件描述符中就不见了。而文件的状态标志则是针对每个文件的,不管索引到该文件的文件描述符是多少。当我们调用了dup2或者dup之后,文件描述符标志中的FD_CLOEXEC标志会被清除掉。FD_CLOEXEC标志被设置的作用是当我们调用exec函数簇的时候,对应的设置了该标志的文件描述符会在执行exec指定的文件前被关闭。
具体是什么情况由于和我们的目的不同,就不在这里阐述了。(当我们调用exec之后,不相让exec执行的程序访问原来进程打开的文件描述符所指向的文件时,就应该设置该标志)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值