什么是文件描述符(fd)与cat的实现
概要:讲解文件描述符(fd)模型的核心思想:统一接口、进程隔离与可组合性,并结合 cat 的实现说明 fd 如何让文件、管道等对象统一通过 read/write 访问。
核心思想
文件描述符模型解决了三个问题:
-
统一接口
file / pipe / socket -> read/write -
进程隔离
每个进程独立 fd 表 -
可组合性
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)
行为:
- 从 fd 指向的文件读取
- 从当前偏移量 offset 读
- 读完后 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 的最小实现。
留下评论