计算机原理101:进程和线程的区别

Posted by FridayLi on March 1, 2025

计算机原理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 是引用,指向装箱后的对象
类字段为值类型 值类型字段在堆中(因为类实例整体在堆上)
闭包中捕获的局部变量 会被提取到堆上