文件描述符(File Descriptor)

一、介绍

内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。

二、功能

文件描述符

文件描述符(File Descriptor)是一个整型,可以理解为一个指向文件的指针。需要注意的就是,一个进程在结束的时候会关闭所有打开的文件描述符,但是0,1,2除外。0,1,2,分别代表标准输入流,标准输出流和标准错误流。这3个文件描述符是由kernel打开和进行管理的,不需要我们来打开和关闭。

文件描述符表

每个表项包含一个指向已打开的文件的指针,如下图所示

  • 每个进程的文件描述符表是一个数组,数组的索引是文件描述符(fd),数组的值是指向 文件表 中某个条目的指针。

  • 文件表是内核维护的全局数据结构,包含文件的打开模式、读写位置等信息。


当调用open打开一个文件或创建一个新文件时,内核分配一个文件描述符并返回给用户程序,该文件描述符表项中的指针指向新打开的文件。当读写文件时,用户程序把文件描述符传给read或write,内核根据文件描述符找到相应的表项,再通过表项中的指针找到相应的文件。

我们知道,程序启动时会自动打开三个文件:标准输入、标准输出和标准错误输出。在C标准库中
分别用FILE *指针stdin、 stdout和stderr表示。这三个文件的描述符分别是0、 1、 2,保存在相应
的FILE结构体中。这3个文件描述符是由kernel打开和进行管理的,不需要我们来打开和关闭。

头文件unistd.h中有如下的宏定义来表示这三个文件描述符:

 

三、文件描述符的创建

文件描述符是通过系统调用创建的,常见的系统调用包括:

  • open():打开文件,返回文件描述符。

  • socket():创建套接字,返回文件描述符。

  • pipe():创建管道,返回两个文件描述符(一个用于读,一个用于写)。

  • dup()dup2():复制文件描述符。

示例:使用 open() 创建文件描述符

四. 文件描述符的复制

  • dup():复制一个文件描述符,返回一个新的文件描述符。

  • dup2():复制一个文件描述符,并指定新的文件描述符编号。

示例:使用 dup2() 重定向标准输出

五. 文件描述符的关闭

  • 使用 close() 系统调用关闭文件描述符。

  • 关闭文件描述符会释放相关资源,并将其从文件描述符表中移除。

示例:关闭文件描述符

六. 文件描述符的限制

  • 每个进程的文件描述符数量是有限制的。

可以通过以下命令查看当前系统的文件描述符限制:

  • ulimit -n

  • 可以通过修改 /etc/security/limits.conf 或使用 ulimit 命令调整限制。

七. 文件描述符与 I/O 多路复用

在 Linux 中,文件描述符是 I/O 多路复用(如 selectpollepoll)的基础。这些机制允许程序同时监控多个文件描述符的状态。

示例:使用 select 监控文件描述符

八. 文件描述符与进程间通信

文件描述符在进程间通信(IPC)中扮演重要角色,例如:

  • 管道(pipe):通过 pipe() 创建两个文件描述符,一个用于读,一个用于写。

  • 套接字(socket):通过 socket() 创建文件描述符,用于网络通信。

示例:使用管道进行进程间通信

九. 文件描述符的高级用法

  • fcntl():用于控制文件描述符的属性,例如设置非阻塞模式。

  • ioctl():用于设备文件的控制操作。

  • sendfile():高效地在文件描述符之间传输数据。

示例:使用 fcntl() 设置非阻塞模式

十、总结

文件描述符是 Linux 中管理文件和 I/O 操作的核心概念。通过文件描述符,程序可以访问文件、管道、套接字等资源。理解文件描述符的工作原理对于编写高效的 Linux 程序至关重要。

 

 

参考:

文件描述符(File Descriptor)简介-CSDN博客

### 文件描述符及 `FILE` 结构体的概念 #### 文件描述符 文件描述符是一个整数,用于标识操作系统中已打开的文件或 I/O 资源。它提供了一种抽象机制来处理各种类型的输入输出操作。在 Unix 和 Linux 系统中,每个进程都有自己的文件描述符表,其中包含了该进程中所有打开文件的信息。 #### `FILE` 结构体 `FILE` 是标准 C 库中的一个不透明数据类型,表示流(stream),通常用来封装底层的文件描述符和其他控制信息。通过 `FILE*` 指针可以方便地进行高级别的文件读写操作。要从 `FILE` 对象获得其对应的文件描述符,可使用 `fileno()` 函数[^1]。 ```c #include <stdio.h> int main() { FILE *fp; fp = fopen("example.txt", "r"); if (fp != NULL) { int fd = fileno(fp); printf("File descriptor of example.txt is %d\n", fd); fclose(fp); } } ``` ### 使用文件描述符的操作 对于低级别的 I/O 操作,可以直接利用文件描述符来进行更精细的控制。例如,在多线程或多进程编程时,可能需要管理多个并发连接;此时,使用文件描述符集合 (`fd_set`) 来跟踪哪些文件准备好读取或写入会更加高效。下面展示了如何清除特定文件描述符的状态: ```c #include <sys/select.h> #include <unistd.h> void clear_fd(int fd, fd_set *set) { FD_CLR(fd, set); // 将参数文件描述符fd对应标志设置为0 } ``` 此外,在某些情况下,还需要跨进程共享文件资源。这可以通过复制文件描述符或将整个 `struct file` 结构传递给另一个进程实现[^3]。 ### 相关 API 及宏定义 当涉及到不同平台上的兼容性问题时,可以根据编译环境条件编译相应的代码片段。比如针对 Windows 和 POSIX 平台的不同特性做出适配: ```cpp #ifdef _WIN32 #define EXPORT extern "C" __declspec(dllexport) #else // unix/linux #define EXPORT extern "C" #endif EXPORT void some_function() { // 实现细节... } ``` 上述例子展示了一个简单的导出函数声明方式,适用于创建动态链接库(DLL),使得其他程序能够调用此功能模块内的接口[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值