什么是进程的文件描述符File Descriptors?其创建回收和生命周期

在 Linux 和 Unix 系统中,文件描述符(File Descriptor,FD) 是进程用于访问文件、管道、套接字、设备等的一种抽象标识符。本质上,它是一个非负整数,用于标识进程打开的文件或 I/O 资源。
1. 什么是文件描述符?
什么是进程的文件描述符(File Descriptors)?
每个进程都有一个文件描述符表,存储该进程打开的所有文件或 I/O 资源。
文件描述符是进程级别的,每个进程都有自己独立的描述符表。
文件描述符从 0 开始分配,默认有三个标准输入/输出/错误:
文件描述符
名称
作用
默认设备
0
标准输入(stdin)
读取输入数据
键盘
1
标准输出(stdout)
输出正常信息
终端
2
标准错误(stderr)
输出错误信息
终端
2. 文件描述符的生命周期
一个文件描述符的生命周期包括:
分配:进程使用 open()、socket()、pipe() 等系统调用打开文件或资源,系统返回一个文件描述符。
使用:进程通过 read()、write()、send()、recv() 等操作读写文件描述符指向的资源。
关闭:进程调用 close(fd) 释放文件描述符,防止资源泄漏。
3. 文件描述符的创建
(1) open() 打开文件
1234567891011121314
#include <fcntl.h>#include <unistd.h>#include <stdio.h> int main() { int fd = open("test.txt", O_RDONLY); // 以只读模式打开文件 if (fd == -1) { perror("open failed"); return 1; } printf("File Descriptor: %d\n", fd); close(fd); return 0;}
open() 返回的 fd 是进程打开的文件描述符。
close(fd) 关闭文件,释放描述符。
(2) dup() 和 dup2() 复制文件描述符
dup(fd):复制文件描述符,返回新的 FD,指向相同的文件。
dup2(old_fd, new_fd):将 new_fd 复制为 old_fd 的副本。
123456789101112
#include <unistd.h>#include <fcntl.h>#include <stdio.h> int main() { int fd = open("test.txt", O_RDONLY); int new_fd = dup(fd); // 复制文件描述符 printf("Original FD: %d, New FD: %d\n", fd, new_fd); close(fd); close(new_fd); return 0;}
4. 文件描述符的使用
4.1) 标准输入/输出重定向
">" 标准输出重定向
1
echo "Hello World" > output.txt
">" 使 stdout(文件描述符 1)指向 output.txt,不会输出到终端。
"<" 标准输入重定向
1
wc -l < input.txt
"<" 让 stdin(文件描述符 0)从 input.txt 读取,而不是键盘输入。
2> 标准错误重定向
1
ls non_existing_file 2> error.log
2> 让 stderr(文件描述符 2)写入 error.log,不会输出到终端。
&> 同时重定向 stdout 和 stderr
1
command &> output.log
&> 让 stdout 和 stderr 都写入 output.log。
4.2) pipe() 创建管道
123456789101112131415161718192021
#include <unistd.h>#include <stdio.h> int main() { int fd[2]; // fd[0] 读端, fd[1] 写端 pipe(fd); if (fork() == 0) { close(fd[0]); // 子进程关闭读端 write(fd[1], "Hello", 5); close(fd[1]); } else { close(fd[1]); // 父进程关闭写端 char buffer[10]; read(fd[0], buffer, 5); buffer[5] = '\0'; printf("Received: %s\n", buffer); close(fd[0]); } return 0;}
pipe(fd) 创建管道,返回 fd[0](读端)和 fd[1](写端)。
进程通过 read(fd[0]) 读取数据,write(fd[1]) 发送数据。
5. 文件描述符的查看
5.1) ls -l /proc/
1
ls -l /proc/$(pgrep bash)/fd/
查看进程的所有文件描述符。
5.2) lsof 查看进程打开的文件
1
lsof -p $(pgrep bash)
列出 bash 进程打开的所有文件。
5.3)fuser 查看占用文件的进程
1
fuser test.txt
列出访问 test.txt 的所有进程。
6. 文件描述符的限制
6.1) 查看系统允许的最大文件描述符
12
ulimit -n # 查看单个进程最大文件描述符数cat /proc/sys/fs/file-max # 查看系统级最大文件描述符数
6.2) 临时修改文件描述符限制
1
ulimit -n 100000
6.3) 永久修改文件描述符限制
6.3.1) 修改 /etc/security/limits.conf
12
echo "* soft nofile 100000" >> /etc/security/limits.confecho "* hard nofile 200000" >> /etc/security/limits.conf
6.3.2) 修改 /etc/pam.d/common-session
1
echo "session required pam_limits.so" >> /etc/pam.d/common-session
6.3.3) 修改 /etc/systemd/system.conf
1
echo "DefaultLimitNOFILE=100000" >> /etc/systemd/system.conf
7. 文件描述符的回收
进程终止时,操作系统会自动关闭所有打开的文件描述符。
手动回收:使用 close(fd) 释放文件描述符,防止文件描述符泄漏(FD Leak)。
1234567891011121314
#include <fcntl.h>#include <unistd.h>#include <stdio.h> int main() { int fd = open("test.txt", O_RDONLY); if (fd == -1) { perror("open failed"); return 1; } // 进行文件操作 close(fd); // 关闭文件,避免文件描述符泄漏 return 0;}
8. 理解文件描述符对编写高效的 Linux 服务器程序(如 Nginx、Redis) 至关重要
文件描述符是 Linux 进程访问文件、管道、套接字的核心机制。
标准文件描述符(0、1、2)用于输入、输出和错误,可以重定向。
使用 open()、socket()、pipe() 创建文件描述符,close(fd) 释放。
dup() 和 dup2() 复制文件描述符,可用于 I/O 重定向。
使用 ulimit 和 limits.conf 调整文件描述符上限,避免 “Too many open files” 错误。
wer
相关文章:
Linux 进程的详细解释
进程间通信信号(Signals)详解
Linux 进程的构成
Linux系统的构成
linux内核的进程调度策略
Linux系统信息与管理命令清单
必须掌握的 Linux 命令
除教程外,本网站大部分文章来自互联网,如果有内容冒犯到你,请联系我们删除!