一、介绍
内核(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 多路复用(如 select
、poll
、epoll
)的基础。这些机制允许程序同时监控多个文件描述符的状态。
示例:使用 select
监控文件描述符
八. 文件描述符与进程间通信
文件描述符在进程间通信(IPC)中扮演重要角色,例如:
-
管道(pipe):通过
pipe()
创建两个文件描述符,一个用于读,一个用于写。 -
套接字(socket):通过
socket()
创建文件描述符,用于网络通信。
示例:使用管道进行进程间通信
九. 文件描述符的高级用法
-
fcntl()
:用于控制文件描述符的属性,例如设置非阻塞模式。 -
ioctl()
:用于设备文件的控制操作。 -
sendfile()
:高效地在文件描述符之间传输数据。
示例:使用 fcntl()
设置非阻塞模式
十、总结
文件描述符是 Linux 中管理文件和 I/O 操作的核心概念。通过文件描述符,程序可以访问文件、管道、套接字等资源。理解文件描述符的工作原理对于编写高效的 Linux 程序至关重要。
参考:
文件描述符(File Descriptor)简介-CSDN博客