golang,go,博客,开源,编程
在 Linux 系统中,文件描述符表是每个进程用于管理文件描述符(File Descriptors,简称 FD)的一种数据结构。每个进程都有一个独立的文件描述符表,用于记录该进程打开的文件或输入输出资源(如管道、套接字、设备等)以及与之相关的信息。通过文件描述符,进程可以执行各种 I/O 操作,如读取、写入文件,或通过套接字进行网络通信。
文件描述符表是一个内存中的数据结构,其中每个条目(通常是一个结构体)表示一个打开的文件或 I/O 流。每个文件描述符都是文件描述符表中的一个索引,用于访问对应的文件或资源。
struct file
)和文件操作结构体(struct file_operations
)。在 Linux 系统中,进程的文件描述符表通常包含以下几个部分:
索引为0、1、2的标准文件描述符:
这三个文件描述符在进程启动时由操作系统自动打开,并且是预设的。
文件描述符表的动态部分:如果进程打开更多的文件或设备,操作系统会为每个打开的资源分配一个文件描述符,并将它们记录在文件描述符表中。每个文件描述符指向内核中相应的文件描述符结构。
在内核中,文件描述符表记录着进程打开的文件、设备、管道等资源的指针。在 Linux 中,file
结构体是用来表示一个文件描述符的核心结构体。每个文件描述符在内核中都有一个对应的 struct file
结构体。该结构体包含了文件的状态、读写操作以及其他元数据。
// struct file
struct file {
struct file_operations *f_op; // 文件操作函数指针
unsigned int f_flags; // 文件的标志位(如只读、读写等)
off_t f_offset; // 当前文件读写位置(文件指针)
struct inode *f_inode; // 指向文件的 inode
void *private_data; // 特定于文件的私有数据(如套接字文件的私有数据)
};
f_op
:指向 file_operations
结构体的指针,定义了该文件的操作函数,如读写操作。f_flags
:表示文件的标志,指示文件是以只读模式、只写模式,还是读写模式打开的。f_offset
:表示文件的当前读写位置(文件指针),用于文件的随机访问。f_inode
:指向文件的 inode(索引节点),包含文件的元数据(如权限、大小、时间戳等)。private_data
:特定于文件的私有数据,用于存储文件类型的特定数据,如网络套接字相关的数据。操作系统通过系统调用对文件描述符表进行管理,以下是几个常见的文件描述符操作:
open
系统调用)当一个进程调用 open()
打开文件时,操作系统会在文件描述符表中分配一个空闲的文件描述符,并在内核中创建一个 struct file
对象。这个对象包含了文件的操作信息和当前状态。
int fd = open("file.txt", O_RDWR); // 打开文件并返回文件描述符
-1
。fd
映射到 struct file
结构体,并记录在进程的文件描述符表中。read
/ write
系统调用)一旦文件描述符被分配并指向一个文件,进程可以使用 read()
和 write()
函数来进行文件的读写操作。
char buffer[128];
read(fd, buffer, sizeof(buffer)); // 从文件描述符 fd 读取数据
write(fd, buffer, sizeof(buffer)); // 将数据写入到文件描述符 fd 指向的文件
struct file
,然后调用相应的文件操作函数来执行读取操作。struct file
,然后调用文件操作函数来执行写入操作。close
系统调用)当文件不再需要时,进程可以通过 close()
系统调用关闭文件描述符,操作系统会释放文件描述符所占用的资源,并将该文件描述符从文件描述符表中移除。
close(fd); // 关闭文件描述符 fd
close()
后,操作系统会更新进程的文件描述符表,释放对应的文件描述符,并减少该文件的引用计数。当引用计数为零时,内核会释放 struct file
结构体所占用的内存。dup
系统调用)进程还可以使用 dup()
或 dup2()
系统调用复制一个文件描述符,使多个文件描述符指向相同的文件。
int new_fd = dup(fd); // 复制文件描述符 fd
int new_fd2 = dup2(fd, 3); // 将 fd 复制到文件描述符 3
dup()
:复制当前的文件描述符,返回一个新的文件描述符,指向相同的文件。dup2()
:将文件描述符 fd
复制到指定的文件描述符 new_fd2
。通过 Unix 域套接字(Unix Domain Socket),一个进程可以将文件描述符传递给另一个进程。Linux 支持文件描述符的传递,通过 sendmsg()
和 recvmsg()
系统调用来实现。
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
struct msghdr msg;
struct iovec io;
char buf[1];
io.iov_base = buf;
io.iov_len = 1;
msg.msg_iov = &io;
msg.msg_iovlen = 1;
int fd_to_send = open("file.txt", O_RDONLY);
sendmsg(sockfd, &msg, 0); // 传递文件描述符
每个进程都有限制可以打开的文件描述符的数量,这个限制是由操作系统的内核配置和系统资源决定的。通常可以通过以下命令查看和设置文件描述符的最大限制:
ulimit -n # 查看当前进程的文件描述符限制
ulimit -n 1024 # 设置最大文件描述符数为1024
struct file
数据结构管理每个文件描述符的具体信息,包括文件位置、文件标志、文件操作等。open()
, read()
, write()
, close()
等系统调用操作文件描述符。