1 分钟阅读 次阅读

概要:讲解文件描述符(fd)模型的核心思想:统一接口、进程隔离与可组合性,并结合 cat 的实现说明 fd 如何让文件、管道等对象统一通过 read/write 访问。

核心思想

文件描述符模型解决了三个问题:

  1. 统一接口

    file / pipe / socket -> read/write
    
  2. 进程隔离

    每个进程独立 fd 表
    
  3. 可组合性

    pipe + dup + fork + exec
    = shell
    

1. 什么是文件描述符(fd)

文件描述符 = 一个整数句柄,用来表示内核中的一个打开对象。

例如:

fd = 3

这个 3 不是文件本身,而是 索引,指向内核里的一个结构体:

进程
 └── fd table
      0 -> stdin
      1 -> stdout
      2 -> stderr
      3 -> /etc/passwd
      4 -> pipe

在 Linux / xv6 中:

fd 含义
0 标准输入 stdin
1 标准输出 stdout
2 标准错误 stderr

2. 为什么要用文件描述符

Unix 的设计哲学:

Everything is a file(万物皆文件)

文件描述符统一抽象了:

对象 是否使用 fd
普通文件
终端
管道
socket
设备

因此系统调用只有两个:

read(fd)
write(fd)

不需要:

read_file()
read_pipe()
read_socket()

这就是 Unix I/O 强大的地方。


3. 每个进程都有自己的 fd 表

内核中:

struct proc {
    struct file *ofile[NOFILE];
}

所以:

进程A: fd=3 -> file1
进程B: fd=3 -> file2

fd 是 进程私有的


4. read 和 write 的工作方式

read(fd, buf, n)

行为:

  1. 从 fd 指向的文件读取
  2. 从当前偏移量 offset 读
  3. 读完后 offset 自动增加

示意:

文件: ABCDEFG
offset=0

read(3 bytes) -> ABC
offset=3

read(3 bytes) -> DEF
offset=6

当读到文件末尾:

read() 返回 0

这是判断 EOF 的唯一标准。


5. write 也是同样逻辑

write(fd, buf, n)
  • 从当前 offset 写
  • 写完 offset 增加

6. cat 的本质实现

  • 示例程序 ```c char buf[512]; int n;

for(;;){ n = read(0, buf, sizeof buf); if(n == 0) break; if(n < 0){ fprintf(2, “read error\n”); exit(); } if(write(1, buf, n) != n){ fprintf(2, “write error\n”); exit(); } }


这段代码:

```c
char buf[512];
int n;

for(;;){
    n = read(0, buf, sizeof buf);

意思是:

从 stdin 读

接着:

write(1, buf, n);
写入 stdout

也就是:

stdin -> stdout

这就是 cat 的最小实现

留下评论