fork复制后文件描述符映射关系

本文详细介绍了文件描述符的概念、工作原理及其在进程间的行为,包括如何使用open、close等函数与文件关联,以及文件描述符在进程继承、共享和独占操作中的角色。

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

在C程序中,文件由文件指针或者文件描述符表示。ISO C的标准I/0库函数(fopen, fclose, fread, fwrite, fscanf,fprintf等)使用文件指针,UNIX的I/O函数(open, close, read, write, ioctl)使用文件描述符。下面重点来说下,文件描述符是如何工作的。

文件描述符相当于一个逻辑句柄,而open,close等函数则是将文件或者物理设备与句柄相关联。句柄是一个整数,可以理解为进程特定的文件描述符表的索引。先介绍下面三个概念,后面讲下open、close等操作以后,文件和文件描述符产生什么关系,以及fork后文件描述符的继承等问题。




文件描述符表:用户区的一部分,除非通过使用文件描述符的函数,否则程序无法对其进行访问。对进程中每个打开的文件,文件描述符表都包含一个条目。
系统文件表:为系统中所有的进程共享。对每个活动的open, 它都包含一个条目。每个系统文件表的条目都包含文件偏移量、访问模式(读、写、or 读-写)以及指向它的文件描述符表的条目计数。
内存索引节点表: 对系统中的每个活动的文件(被某个进程打开了),内存中索引节点表都包含一个条目。几个系统文件表条目可能对应于同一个内存索引节点表(不同进程打开同一个文件)。
1、举例: 执行myfd = open("/home/lucy/my.dat", O_RDONLY); 以后,上述3个表的关系原理图如下:

系统文件表包含一个偏移量,给出了文件当前的位置。若2个进程同时打开一个文件(如上图A,B)做读操作,每个进程都有自己相对于文件的偏移量,而且读入整个文件是独立于另一个进程的;如果2个进程打开同一个文件做写操作,写操作是相互独立的,每个进程都可以重写另一个进程写入的内容。
如果上面进程在open以后又执行了close()函数,操作系统会删除文件描述符表的第四个条目和系统文件表的对应条目(若指向它的描述符表唯一),并对内存索引节点表条目中的计数减1,如果自减以后变为0,说明没有其他进程链接此文件,将索引节点表条目也删除,而这里进程B也在open这个文件,所 以索引节点表条目保留。

2、文件描述符的继承

通过fork()创建子进程时,子进程继承父进程环境和上下文的大部分内容的拷贝,其中就包括文件描述符表。

(1)对于父进程在fork()之前打开的文件来说,子进程都会继承,与父进程共享相同的文件偏移量。如下图所示(0-1-2 表示 标准输入-输出-错误):


系统文件表位于系统空间中,不会被fork()复制,但是系统文件表中的条目会保存指向它的文件描述符表的计数,fork()时需要对这个计数进行维护, 以体现子进程对应的新的文件描述符表也指向它。程序关闭文件时,也是将系统文件表条目内部的计数减一,当计数值减为0时,才将其删除。
(2)相反,如果父进程先进程fork,再打开my.dat,这时父子进程关于my.dat 的文件描述符表指向不同的系统文件表条目,也不再共享文件偏移量(fork以后2个进程分别open,在系统文件表中创建2个条目);但是关于标准输入, 标准输出,标准错误,父子进程还是共享的。

 


Ps:

1.      只有文件是唯一的,对应的其在内存中的索引节点表是唯一的

 

2.      同一个进程内可能会有多个描述符file1file2等在指向同一个系统文件表,不同进程(父子进程)也有可能有描述符指向同一个系统文件表,但关闭某个描述符不会立马删除系统文件表导致其他描述符无法寻址,关闭系统文件表依赖于其关于文件描述符的计数

 

3.      同理,不同进程可能会打开同一个文件,其产生的结果是不同的系统文件表项指向同一个内存索引节点。删除某个系统文件表项不一定就会删除其内存索引节点,节点的删除依赖于计数

 

 

也就是说,对于无关的不同进程,其区别在系统文件表级别(对应的描述符一定不同),对于fork出来的子进程,其区别在于文件描述符级别。

<think>好的,用户现在问的是“文件描述符是什么,它指向什么”。之前他们问了关于进程打开文件的读写指针,现在这个问题看起来是相关的,可能是在学习操作系统或文件系统相关的知识。 首先,我需要明确文件描述符的基本定义。文件描述符是一个整数,用来标识进程打开的文件或其他I/O资源。但用户可能想知道更深层次的内容,比如它具体指向什么结构,以及操作系统如何管理这些描述符。 接下来,用户可能已经知道读写指针,所以需要把文件描述符和之前的读写指针联系起来。比如,每个文件描述符对应一个文件对象,而读写指针是文件对象的一部分。这时候需要解释清楚文件描述符和文件对象的关系,以及它们在内核中的结构。 还要考虑用户可能的疑惑点,比如为什么文件描述符是整数,不同的描述符如何指向同一个文件,或者复制描述符(如dup)后的行为差异。这些都需要在回答中涵盖,可能通过例子说明。 另外,用户可能想知道文件描述符的应用场景,比如在进程间通信、网络编程中的作用,比如套接字也是通过文件描述符来管理的。这时候需要扩展一下,说明文件描述符不仅用于普通文件,还用于管道、套接字等资源。 要注意结构清晰,分点回答,可能分成定义、指向内容、内核结构、管理方式、常见问题等部分。同时,使用用户能理解的语言,避免过于专业的术语,必要时用比喻帮助理解,比如把文件描述符比作索引或门票。 还要检查是否有常见的误解需要澄清,比如文件描述符是否直接指向文件本身,或者多个描述符指向同一个文件时的行为差异。这时候需要强调文件描述符和文件对象的关系,以及不同描述符之间的独立性或共享性。 最后,可能需要总结文件描述符的重要性,比如在进程资源管理中的作用,或者如何通过系统调用操作它们。确保回答覆盖了用户的问题,并且链接到之前关于读写指针的内容,帮助用户形成完整的知识体系。</think>--- #### **1. 文件描述符的定义** **文件描述符**(File Descriptor,简称 **fd**)是操作系统为**进程**分配的**非负整数**(如0、1、2等),用于唯一标识进程打开的**文件或I/O资源**。 - **本质**:是进程级资源句柄,通过它可以访问文件、管道、套接字、设备等。 - **作用**:相当于进程操作文件的“门票”,所有读写操作必须通过文件描述符完成。 --- #### **2. 文件描述符指向什么?** 文件描述符并不直接指向文件本身,而是指向内核中的**文件对象**(File Object)。 ##### **内核数据结构关系** 1. **进程级表**(进程控制块PCB): - 每个进程维护一个**文件描述符表**,表中每一项记录指向内核文件对象的指针。 - 例如:`fd=3` → 指向某个`struct file`对象。 2. **系统级表**: - **文件对象**(struct file):记录文件的打开模式(读/写)、读写指针位置、引用计数等。 - **inode**:指向文件元数据(如权限、大小、磁盘位置等)。 ![文件描述符内核结构示意图](https://via.placeholder.com/600x200?text=进程文件描述符表 → 内核文件对象 → 文件inode) --- #### **3. 文件描述符的分配规则** - **默认分配**: - `0`:标准输入(STDIN_FILENO) - `1`:标准输出(STDOUT_FILENO) - `2`:标准错误(STDERR_FILENO) - **新文件描述符**:由`open()`、`socket()`等系统调用返回,通常从当前最小未使用的整数开始分配(如3、4、5…)。 --- #### **4. 文件描述符的特性** | **特性** | **说明** | |-------------------|-------------------------------------------------------------------------| | **进程隔离** | 不同进程的相同fd值可能指向不同文件(如进程A的fd3 ≠ 进程B的fd3)。 | | **共享性** | 父子进程通过`fork()`继承文件描述符表(共享同一文件对象和读写指针)。 | | **独立性** | 同一进程多次`open()`同一文件,会生成多个独立的文件描述符和文件对象。 | | **资源限制** | 系统限制单个进程可打开的最大fd数量(通过`ulimit -n`查看)。 | --- #### **5. 文件描述符的常见操作** ##### **(1) 打开文件获取fd** ```c int fd = open("test.txt", O_RDWR); // 返回新分配的fd(如3) ``` ##### **(2) 复制fd(共享文件对象)** ```c int fd2 = dup(fd); // 复制fd,新旧fd共享同一文件对象(读写指针同步变化) ``` ##### **(3) 关闭fd** ```c close(fd); // 释放fd,减少文件对象的引用计数。若引用计数为0,内核释放文件对象。 ``` --- #### **6. 文件描述符与文件对象的关系** - **一对一 vs 一对多**: - 多个fd可以指向同一个文件对象(如通过`dup()`复制)。 - 同一文件被多次`open()`时,每个fd对应独立的文件对象。 - **示例**: ```c fd1 = open("a.txt", O_RDONLY); // 创建新文件对象A fd2 = open("a.txt", O_WRONLY); // 创建新文件对象B(独立于A) fd3 = dup(fd1); // 复制fd1,fd3与fd1共享文件对象A ``` --- #### **7. 文件描述符的应用场景** 1. **普通文件读写**:通过`read()`/`write()`操作文件。 2. **进程间通信**: - 管道(`pipe(fd[2])`)使用fd[0]读、fd[1]写。 - 套接字(Socket)通过fd进行网络通信。 3. **设备操作**:如操作终端设备(`/dev/tty`)。 --- #### **8. 常见问题** - **Q:文件描述符和文件句柄(File Handle)有什么区别?** A:在Linux中两者通常指同一概念,但在Windows中“句柄”更广义(包括窗口、内存对象等)。 - **Q:为什么标准输入/输出/错误的fd是0、1、2?** A:历史约定,由POSIX标准定义,进程启动时自动分配。 - **Q:文件描述符耗尽怎么办?** A:进程会收到`EMFILE`错误,需检查是否有未关闭的fd,或调整系统限制(`ulimit -n`)。 - **Q:fd是否跨进程共享?** A:否!即使父子进程继承fd表,各自的fd仍指向同一文件对象(读写指针共享)。 --- #### **9. 总结** - **文件描述符是进程访问I/O资源的入口**,本质是内核文件对象的引用标识。 - 它通过层级映射(进程fd表 → 内核文件对象 → inode)实现对文件的操作。 - 理解fd的共享性、独立性及生命周期,对开发高并发、高效文件操作程序至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值