主题
21 · 文件描述符、管道与重定向
目标:把
>、|、stdin/out/err 映射到进程的 fd 表,并用/proc/PID/fd验证。
命令行前置:06 · 管道;Shell:11。
1. 环境前提
bash
mkdir -p ~/lab/os/fd1
2. 概念框(轻量课本)
| 词 | 一句话 |
|---|---|
| 文件描述符(fd) | 进程里的小整数,指向内核中的「已打开文件」对象 |
| 0 / 1 / 2 | 标准输入 / 标准输出 / 标准错误(约定) |
| 重定向 | 把某个 fd 改接到文件或另一个 fd |
| 管道(pipe) | 内核提供的单向字节通道;两端各是一个 fd |
text
进程 fd 表 内核对象
0 ─────────────► 终端(读)
1 ─────────────► 终端(写)
2 ─────────────► 终端(写)
3 ─────────────► 某文件 / socket / pipe 端1
2
3
4
5
2
3
4
5
3. 动手:看自己的 fd
bash
ls -l /proc/$$/fd
# 常见:0 1 2 → /dev/pts/N 或类似1
2
2
重定向实验:
bash
# 1 接到文件;2 仍在终端
echo out > ~/lab/os/fd/out.txt
echo err >&2
# 合并 2 到 1 再进文件(Bash)
{ echo out; echo err >&2; } > ~/lab/os/fd/both.txt 2>&1
cat ~/lab/os/fd/both.txt1
2
3
4
5
6
7
2
3
4
5
6
7
管道:
bash
# 左侧的 stdout(1) 接到管道写端;右侧 stdin(0) 接到读端
echo "hello pipe" | cat
# 另开观察(可选):跑一个长管道时看 fd
sleep 30 | cat &
BPID=$!
# sleep 在管道左;用 pstree / pgrep 找 PID 后:
# ls -l /proc/<pid>/fd
kill "$BPID" 2>/dev/null || true1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
脚本里常见模式(对照 11~14):
bash
log_error() { echo "$*" >&2; } # 错误走 fd 2,不污染管道数据1
4. 与命令行篇对照
| 06 / Shell | OS 视角 |
|---|---|
cmd > file | 打开文件,把 fd 1 指过去 |
cmd 2> file | 改 fd 2 |
a | b | 建 pipe,串起两个进程的 1 与 0 |
tee | 同时写文件与 stdout(多路 fd) |
5. 动手清单
- [ ]
ls -l /proc/$$/fd认出 0/1/2 - [ ] 写出
> file与2>&1各改了谁 - [ ] 用管道跑一条命令,口述两端 fd 如何相连
- [ ] 解释为何错误信息应用
>&2
6. 常见翻车
| 翻车 | 处理 |
|---|---|
cmd > f 2>&1 与 cmd 2>&1 > f 顺序 | 从左到右生效,结果不同;记常用「先 >f 再 2>&1」 |
管道后 grep 不到错误 | 错误在 stderr,未进管道 |
| fd 泄漏 | 长期跑的服务打开文件不关;用 /proc/PID/fd 排查 |
7. 官方 / 延伸
man bash(REDIRECTION)man 7 pipe、man 2 open
下一篇:套接字、端口与网络栈直觉。
