计算机原理101: 进程和线程的区别
《计算机底层的秘密》读书笔记 + GPT问答整理
进程是操作系统分配资源的单位, 线程是调度的基本单位, 线程之间共享进程资源。
1.多线程的进程地址空间是什么样的?
CPU可以把PC寄存器指向main函数,也可以把PC寄存器指向任何一个函数从而常见一个新的执行流。每个执行流就是一个线程。
每个线程 私有 的部分:
- 栈(Stack),用于局部变量、函数调用、返回地址等
- 寄存器上下文, 包括程序计数器(PC)、栈指针等
- 线程ID,用于调度管理和标识线程
2.线程间到底共享了哪些进程资源?同一个进程内的多个线程,为什么可以共享资源?
- 数据区, 存放的是全局变量。
- 堆区, malloc/new申请的内存
- 代码区, 这部分只读
因为同一个进程的所有线程都运行在同一个“虚拟地址空间”中,所以它们可以共享全局变量、堆、代码段等资源。
为什么不同进程的线程不能互访? ▶️ 本质原因:虚拟内存隔离
- 操作系统为每个进程分配一个独立的虚拟地址空间
- 虽然进程 A 和进程 B 的变量可能都在 0x1000 地址,但它们映射到的物理内存是不同的
- 线程是进程内的执行单元,只能看到所属进程的虚拟内存
- 因此,一个线程无法访问另一个进程的内存变量
3.如何做到线程安全?
线程使用私有资源, 能实现安全。 线程使用共享资源,在不影响其他线程的约束下使用共享资源也能实现线程安全。比如各种锁, 信号量。
4.CPU是如何调度多个线程的?单核和多核有什么不同?
一个核心一次只能执行一个线程指令流(除非启用了超线程/SMT)。 ▶️ 情况 1:单核 CPU + 多线程程序
- 实际上 同一时间只有一个线程在运行
- 操作系统使用“线程调度器”让多个线程 轮流占用 CPU
- 每个线程运行一段时间(时间片),然后切换到另一个线程 切换过程称为:上下文切换
▶️ 情况 2:多核 CPU + 多线程程序
- 每个 CPU 核心可以同时运行一个线程
- 如果你有 4 核 CPU,最多可以真正同时执行 4 个线程
- 超过 4 个的线程仍需轮换
现在的主流操作系统(如 Windows、Linux、macOS)在运行调度时,调度的单位是“线程(Thread)”,而不是整个进程。
多个核心可以同时执行一个进程下的多个线程, 也可以同时执行不同进程下的多个线程。
5.每个进程里都有一个主线程吗?
是的,每个进程在创建时都会自动拥有一个主线程(main thread),这是程序开始执行的地方。
主线程是指: 进程启动后,由操作系统创建的第一个线程,它负责从 main() 函数或等效入口开始执行程序逻辑。 主线程结束后,进程是否结束取决于是否有其它线程还在运行。 主线程通常代表程序主逻辑 子线程用于辅助执行任务 主线程退出可能导致整个进程结束,子线程退出不会结束进程(除非是最后一个线程)。
6. Python中哪些东西是存放在堆区?
在 Python 中,绝大多数对象都是存储在“堆区(Heap)”的。这一点和 C/C++ 不同(它们区分栈上对象和堆上对象),Python 是一种高度抽象和面向对象的语言,内部采用“统一的堆分配机制”。
存储内容 | 位置 |
---|---|
所有对象(int、list、class) | ✅ 堆区 |
变量名 / 引用 | ⚠️ 栈帧中 |
函数参数 / 局部变量名 | ⚠️ 栈帧中 |
闭包变量 / cell 对象 | ✅ 堆区 |
7. C#中哪些东西是存放在堆上的?
C# 是一门有 明确堆(heap)与栈(stack)内存模型的托管语言,你可以通过值类型 vs 引用类型来清晰地判断对象在内存中的分布。
类型/语法 | 存储位置说明 |
---|---|
int x = 5; |
x 在栈上 |
string s = "hi"; |
s 是引用在栈上,字符串内容在堆上 |
new Person() |
实例在堆上,引用在栈上 |
struct Point p = new Point(); |
如果是局部变量,整个结构体在栈上 |
object o = x; (装箱) |
x 被复制到堆中,o 是引用,指向装箱后的对象 |
类字段为值类型 | 值类型字段在堆中(因为类实例整体在堆上) |
闭包中捕获的局部变量 | 会被提取到堆上 |