Menu Close

什么是进程的文件描述符(File Descriptors)?

Linux 和 Unix 系统中,文件描述符(File Descriptor,FD)进程用于访问文件、管道、套接字、设备等的一种抽象标识符。本质上,它是一个非负整数,用于标识进程打开的文件或 I/O 资源。


1. 什么是文件描述符?

什么是进程的文件描述符(File Descriptors)?
什么是进程的文件描述符(File Descriptors)?
  • 每个进程都有一个文件描述符表,存储该进程打开的所有文件或 I/O 资源。
  • 文件描述符是进程级别的,每个进程都有自己独立的描述符表。
  • 文件描述符从 0 开始分配,默认有三个标准输入/输出/错误:
文件描述符 名称 作用 默认设备
0 标准输入(stdin) 读取输入数据 键盘
1 标准输出(stdout) 输出正常信息 终端
2 标准错误(stderr) 输出错误信息 终端

2. 文件描述符的生命周期

一个文件描述符的生命周期包括:

  1. 分配进程使用 open()socket()pipe() 等系统调用打开文件或资源,系统返回一个文件描述符。
  2. 使用进程通过 read()write()send()recv() 等操作读写文件描述符指向的资源。
  3. 关闭进程调用 close(fd) 释放文件描述符,防止资源泄漏。

3. 文件描述符的创建

(1) open() 打开文件

#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 的副本。
#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) 标准输入/输出重定向

">" 标准输出重定向

echo "Hello World" > output.txt

">" 使 stdout(文件描述符 1)指向 output.txt,不会输出到终端。

"<" 标准输入重定向

wc -l < input.txt

"<"stdin(文件描述符 0)从 input.txt 读取,而不是键盘输入。

2> 标准错误重定向

ls non_existing_file 2> error.log

2>stderr(文件描述符 2)写入 error.log,不会输出到终端。

&> 同时重定向 stdoutstderr

command &> output.log

&>stdoutstderr 都写入 output.log

4.2) pipe() 创建管道

#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]) 发送数据。
READ  Linux 进程的构成

5. 文件描述符的查看

5.1) ls -l /proc/<PID>/fd/

ls -l /proc/$(pgrep bash)/fd/

查看进程的所有文件描述符。

5.2) lsof 查看进程打开的文件

lsof -p $(pgrep bash)

列出 bash 进程打开的所有文件。

5.3)fuser 查看占用文件的进程

fuser test.txt

列出访问 test.txt 的所有进程

6. 文件描述符的限制

6.1) 查看系统允许的最大文件描述符

ulimit -n  # 查看单个进程最大文件描述符数
cat /proc/sys/fs/file-max  # 查看系统级最大文件描述符数

6.2) 临时修改文件描述符限制

ulimit -n 100000

6.3) 永久修改文件描述符限制

6.3.1) 修改 /etc/security/limits.conf

echo "* soft nofile 100000" >> /etc/security/limits.conf
echo "* hard nofile 200000" >> /etc/security/limits.conf

6.3.2) 修改 /etc/pam.d/common-session

echo "session required pam_limits.so" >> /etc/pam.d/common-session

6.3.3) 修改 /etc/systemd/system.conf

echo "DefaultLimitNOFILE=100000" >> /etc/systemd/system.conf

7. 文件描述符的回收

  • 进程终止时,操作系统会自动关闭所有打开的文件描述符
  • 手动回收:使用 close(fd) 释放文件描述符,防止文件描述符泄漏(FD Leak)。
#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 重定向。
  • 使用 ulimitlimits.conf 调整文件描述符上限,避免 “Too many open files” 错误。

wer

除教程外,本网站大部分文章来自互联网,如果有内容冒犯到你,请联系我们删除!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Leave the field below empty!

Posted in 进程

Related Posts