Appearance
题目
若进程 P 中有一个线程 T,打开文件后获得 fd,再创建线程 Ta、Tb,则线程 Ta、Tb 可共享的资源是( )。
Ⅰ. 进程 P 的地址空间 Ⅱ. 线程 T 的栈 Ⅲ. fd
错因
A
不确定 fd 是否可以共享——"fd 是 T 打开的,会不会只属于 T?"于是只敢勾选 Ⅰ。但 fd 是进程级资源:内核里只有一张 per-process 文件描述符表,fd 就是这张表的下标,不属于哪个线程私有。任何同进程的线程都能用同一个 fd 读写,这正是多线程并发 I/O 的基础。
C
把"地址空间"和"栈"搞反了——以为线程共享栈、不共享地址空间。但线程"轻量"特性恰恰来自共享地址空间(堆、全局变量、代码段都共用),只有栈和寄存器是线程私有的。Ⅰ Ⅱ 反过来记是经典错误,需特别注意。
D
没注意 Ⅱ 写的是"T 的栈"——以为题意是"T 的所有资源都被 Ta、Tb 继承共享",于是三项全选。但栈是线程私有资源——T 的栈只属于 T,Ta 和 Tb 各有自己独立的栈。如果 Ta 能访问 T 的栈,函数调用层次会互相破坏,根本无法工作。
总解析
核心知识点:线程的私有 vs 共享资源——
| 类别 | 资源 |
|---|---|
| 线程私有 | 栈、寄存器、PC、TCB(线程控制块)、TLS(线程局部存储) |
| 进程级共享(同进程线程共享) | 地址空间(堆、全局变量、代码段、数据段)、文件描述符表、信号处理器、当前工作目录、UID/GID |
逐项核验:
| 项 | 描述 | 共享? | 理由 |
|---|---|---|---|
| Ⅰ | 进程 P 的地址空间 | ✓ | 进程级资源,所有线程共享 |
| Ⅱ | 线程 T 的栈 | ✗ | 栈是线程私有,T 的栈只能 T 自己用 |
| Ⅲ | fd | ✓ | fd 在进程级文件描述符表中,所有线程通用 |
关键陷阱在 Ⅱ:题面说的是"T 的栈"——栈本来就是线程私有的,T 的栈是 T 独占,Ta 和 Tb 各有自己的栈。问"Ta、Tb 共享 T 的栈"——这个问法本身就站不住,因为栈不是设计来共享的。
为什么 fd 可以共享:
进程 P
├── 文件描述符表(进程级,唯一)
│ ├── fd=3 → 指向 /tmp/foo 的 file 结构
│ └── ...
├── 线程 T(私有栈、寄存器)
├── 线程 Ta(私有栈、寄存器)
└── 线程 Tb(私有栈、寄存器)T 调用 open() 拿到的 fd,本质上是 P 的 fd 表里的一个下标——这张表是 P 的,不是 T 的。Ta 和 Tb 直接 read(fd, ...)、write(fd, ...) 都能用同一个 fd,这正是多线程并发 I/O 的常见模式(如多线程 Web Server 共享 socket fd 处理同一个连接)。
速记:
- 共享 = "属于进程的"(地址空间、文件描述符表)
- 私有 = "属于线程的"(栈、寄存器、PC)
- 题面给出的"T 的栈"——名字里就带"T 的",私有无疑
最终答案是 B。