103 lines
3.5 KiB
Markdown
103 lines
3.5 KiB
Markdown
|
|
---
|
||
|
|
tags: [笔试, 微派, 操作系统, 线程, 内存]
|
||
|
|
create time: 2026-05-16 15:00
|
||
|
|
---
|
||
|
|
|
||
|
|
# 06 - 单选题 4:线程的资源隔离
|
||
|
|
|
||
|
|
## 题目
|
||
|
|
|
||
|
|
在同一进程中创建的多个线程之间,以下哪一项资源是**不共享**的?
|
||
|
|
|
||
|
|
| 选项 | 内容 |
|
||
|
|
|------|------|
|
||
|
|
| A | 堆(Heap)内存 |
|
||
|
|
| B | 文件描述符表 |
|
||
|
|
| C | 栈(Stack)空间 |
|
||
|
|
| D | 信号处理函数 |
|
||
|
|
|
||
|
|
<details>
|
||
|
|
<summary>点击查看答案与解析</summary>
|
||
|
|
|
||
|
|
### ✅ 正确答案:**C**
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 详细解析
|
||
|
|
|
||
|
|
#### 进程 vs 线程的资源划分
|
||
|
|
|
||
|
|
```mermaid
|
||
|
|
graph TD
|
||
|
|
%%{init: {'theme':'base','themeVariables': {'tertiaryColor':'#f0f4f8'}}}%%
|
||
|
|
P["🏢 进程地址空间"]
|
||
|
|
|
||
|
|
subgraph SH ["▸ 所有线程共享"]
|
||
|
|
HEAP["📦 堆 Heap"]
|
||
|
|
DATA["📄 数据段<br/>全局/静态变量"]
|
||
|
|
BSS["📜 BSS 段<br/>未初始化全局变量"]
|
||
|
|
CODE["🔧 代码段 .text"]
|
||
|
|
FD["📋 文件描述符表"]
|
||
|
|
SIG["⚙️ 信号处理函数"]
|
||
|
|
CWD["🏠 当前工作目录"]
|
||
|
|
end
|
||
|
|
|
||
|
|
subgraph PT ["▸ 每个线程独有"]
|
||
|
|
ST1["📚 栈 Stack"]
|
||
|
|
REG["🎛️ 寄存器集合"]
|
||
|
|
PC["💻 程序计数器 PC"]
|
||
|
|
TLS["🔒 线程本地存储 TLS"]
|
||
|
|
ERRNO["🚩 错误号 errno"]
|
||
|
|
end
|
||
|
|
|
||
|
|
SEP["━━━━━━━━ 分 隔 ━━━━━━━━"]
|
||
|
|
|
||
|
|
ALL[". . . 所有线程可见 . . ."]
|
||
|
|
T1["线程 1"]
|
||
|
|
T2["线程 2"]
|
||
|
|
T3["线程 3"]
|
||
|
|
|
||
|
|
SH --> ALL
|
||
|
|
PT --> T1
|
||
|
|
PT --> T2
|
||
|
|
PT --> T3
|
||
|
|
|
||
|
|
style SH fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
|
||
|
|
style PT fill:#fff3e0,stroke:#ff9800,color:#e65100
|
||
|
|
style SEP stroke-dasharray: 5 5,fill:#fafafa,stroke:#bbb
|
||
|
|
style ALL stroke:none,fill:none,color:#999
|
||
|
|
style T1,T2,T3 stroke:none,fill:none,color:#999
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 逐项分析
|
||
|
|
|
||
|
|
| 选项 | 正误 | 原因 |
|
||
|
|
|------|------|------|
|
||
|
|
| A | ❌ 不选 | **堆是共享的**。所有线程通过相同的堆指针访问堆内存,所以动态分配的内存对所有线程可见。这也是为什么多线程并发访问同一堆对象需要加锁 |
|
||
|
|
| B | ❌ 不选 | **文件描述符表是共享的**。`open()` 返回的文件描述符对所有线程有效,一个线程打开的文件可以在另一个线程中关闭 |
|
||
|
|
| C | ✅ **正确** | **每个线程有自己独立的栈空间!** 每个线程调用函数时的局部变量、返回地址、函数参数都在自己的栈上。**互不干扰** |
|
||
|
|
| D | ❌ 不选 | **信号处理函数是共享的**。注册了一个信号处理器后,该进程的所有线程都会响应这个处理逻辑 |
|
||
|
|
|
||
|
|
#### 为什么每线程独立栈?
|
||
|
|
|
||
|
|
这是设计上的必然选择:
|
||
|
|
|
||
|
|
1. **函数调用需要局部变量隔离**: `foo()` 在线程 1 中的 `x = 42` 不应该影响线程 2 中的 `x`
|
||
|
|
2. **返回地址独立**: 每个线程调用同一个函数,返回地址各自不同
|
||
|
|
3. **防止栈溢出牵连其他线程**: 如果一个线程的栈爆了,不会影响其他线程的执行
|
||
|
|
|
||
|
|
#### 对比:进程 vs 线程
|
||
|
|
|
||
|
|
| 资源 | 进程间 | 线程间 |
|
||
|
|
|------|--------|--------|
|
||
|
|
| 地址空间 | ❌ 独立 | ✅ 共享 |
|
||
|
|
| 堆 | ❌ 独立 | ✅ 共享 |
|
||
|
|
| 栈 | ❌ 独立 | ❌ 独立(每线程一块) |
|
||
|
|
| 文件描述符 | ❌ 独立(可inherit via fork) | ✅ 共享 |
|
||
|
|
| 通信成本 | 高 (IPC) | 低(直接读写共享内存) |
|
||
|
|
|
||
|
|
> [!tip] Go 语言的 goroutine 区别
|
||
|
|
> Goroutine 和线程类似——每个 goroutine 有自己的栈。但 Go 使用**可增长的栈**(初始仅 2KB),且调度器从用户态出发,比系统级线程更轻量。
|
||
|
|
|
||
|
|
</details>
|