当前位置: 首页 > news >正文

进程与线程到底差在哪?Linux 内核给出答案

进程与线程到底差在哪?Linux 内核给出答案

“进程是资源分配的单位,线程是调度的单位”——这句话背了无数遍,但你真的理解了吗?今天不从教科书出发,直接翻开 Linux 内核源码:进程和线程在内核里到底是什么?答案可能颠覆你的直觉:它们都是同一个结构体 task_struct

一、教科书里的 PCB 与内核里的 task_struct

操作系统原理课告诉我们:每个进程有一个 PCB(进程控制块),装着状态、寄存器现场、地址空间、打开的文件、优先级……

到了 Linux v6.x 这个工业级内核,PCB 落地成一个具体得不能再具体的结构体——task_struct(定义在include/linux/sched.h)。它是内核里被引用最频繁、最庞大的结构体之一,是理解整个进程子系统的"总钥匙"。

关键字段长这样(节选):

structtask_struct{structthread_infothread_info;/* 内核栈信息 */unsignedint__state;/* 进程状态 */intprio;/* 动态优先级 */conststructsched_class*sched_class;/* 调度类 */structsched_entityse;/* CFS 调度实体 */structmm_struct*mm;/* 内存描述符(地址空间)*//* ... 文件表、信号、命名空间…… */};

二、颠覆认知:线程也是 task_struct

重点来了:在 Linux 眼里,进程和线程没有本质区别——都是 task_struct。那它们差在哪?看mm字段:

  • 进程:每个 task_struct 有独立的mm(地址空间),互不共享
  • 线程:多个 task_struct共享同一个mm,只是各自有独立的栈和寄存器现场

所以"进程 vs 线程"的区别,在内核里就是一句话:共享不共享地址空间。这也是为什么线程也叫"轻量级进程"(LWP)——创建线程不需要复制整个地址空间,只需新建一个 task_struct 并指向同一个 mm。

三、内核栈与 current:怎么找到"我是谁"

每个 task_struct 都配一块内核栈(x86-64 上是 16KB)。内核里到处要"拿到当前任务的 task_struct",这就是current宏做的事。

早期内核把 thread_info 放在栈底,靠"栈指针对齐"算出 task_struct 地址;Linux v6.x 默认CONFIG_THREAD_INFO_IN_TASK,thread_info 直接嵌进 task_struct 首部,current变成读一个每 CPU 的当前任务指针——O(1) 拿到"我是谁"。

这个细节告诉你:工业级代码和教科书模型的差别,往往藏在"性能"和"安全"的取舍里。

四、代码演示:用 Python 模拟 task_struct 与进程/线程

fromdataclassesimportdataclass,field@dataclassclassTaskStruct:pid:intstate:str="TASK_RUNNING"prio:int=120mm:object=None# 内存描述符(地址空间)files:list=field(default_factory=list)parent:object=Nonechildren:list=field(default_factory=list)defcreate_process(pid,parent=None):t=TaskStruct(pid=pid,mm={"code":"0x400000","stack":"0x7fff"},parent=parent)ifparent:parent.children.append(t)returntdefcreate_thread(pid,parent):t=TaskStruct(pid=pid,mm=parent.mm,parent=parent)# 共享 mm!parent.children.append(t)returnt main=create_process(1000)t1=create_thread(1001,main)# 线程:共享地址空间p1=create_process(1002,main)# 进程:独立地址空间print(f"线程 t1 与主进程共享地址空间:{t1.mmismain.mm}")print(f"进程 p1 与主进程地址空间独立:{p1.mmisnotmain.mm}")print(f"内核视角: 三者都是 task_struct, 主进程 children={[c.pidforcinmain.children]}")

运行输出:

线程 t1 与主进程共享地址空间: True 进程 p1 与主进程地址空间独立: True 内核视角: 三者都是 task_struct, 主进程 children=[1001, 1002]

这个模型虽然简化,但抓住了内核视角的本质:进程和线程都是 task_struct,区别只在 mm 是否共享

五、避坑清单

  1. "线程是调度的单位"不完全准确:Linux 的调度器调度的是 task_struct(也叫任务),进程和线程都参与调度
  2. 线程切换不一定比进程切换快:同进程线程切换省了地址空间切换(TLB 刷新),但现代 CPU 上这个差距在缩小
  3. 多线程崩溃会带走整个进程:共享地址空间意味着一个线程的野指针可能毁掉所有线程
  4. task_struct 很大(v6.x 上约 7KB):所以内核用 slab 缓存复用,创建线程也要小心分配开销
  5. 看内核源码别被吓到:task_struct 有几百个字段,按语义分段读(调度/内存/文件/信号),一次只读一段

六、想系统学 Linux 内核?

本文精选自ima 知识号【Kruptos】《Linux内核源码精读》订阅库(第 011 期 task_struct 进程在内核里到底是什么、第 012 期 fork/clone/vfork 源码路径等 100 期系统教程,从启动流程、进程管理、内存管理、文件系统到调度器,每期配可运行代码)。

📚 完整系列 100 期 + 配套代码,已在 ima 知识号发布

本文只是系列的一个切片。完整系列(100 期系统教程 + 每期可运行代码)在 ima 知识号【Kruptos】持续更新中:

  • 🗂 67+ 技术知识库:信号与系统、SDR 软件无线电、数字信号处理、操作系统、AI Agent、大模型微调……几乎覆盖全部软硬件技术栈
  • 🧠 8 款 AI 技能:系列生产、知识库管理、CMMI 受管开发、自进化 Agent 等,已在 ima 技能广场上架,即装即用
  • ✅ 全部免费订阅,后续更新自动推送

🔍 订阅方式:打开 ima(腾讯智能工作台)→ 搜索「Kruptos」→ 一键订阅。或在 ima 内直接搜索《Linux内核源码精读》等知识库名称。


作者:Kruptos(西电毕业,13 年无线通信/DSP/嵌入式科研)|原创内容,转载注明出处

http://www.cnnetsun.cn/news/4338826.html

相关文章:

  • IoT设备紧凑型板载电源选型:从LDO到DC-DC的工程实践指南
  • 哪款数据分析工具更好用?2026主流软件全面测评推荐.
  • FMC子卡选型与设计实战:从VITA 57标准到高速I/O布局
  • 音频DAC选型与实战:从R2R到Delta-Sigma,解决噪声与振铃
  • Claude Code 接上 Chrome 之后,前端开发真正形成了 Build Test Fix 闭环
  • 星三角降压启动电路 · 全程精讲
  • FPGA加速卡开发套件实战:PCIe/DDR与高速收发器调试全流程复盘
  • 数据分析工具哪个好用?六类主流平台全维度对比与选型参考
  • 前端工程重试怎样避免放大故障
  • 云原生交付重试怎样避免放大故障
  • 低抖动1.25-GSPS时钟:JESD204B高速数据转换器稳定运行的关键
  • 智能卡读卡器集成实战:从DLL调用到现代化服务架构设计
  • 步进电机原理选型与调试全攻略:解决抖动丢步问题
  • 谱聚类与电气距离:电力系统分区从原理到工程实践
  • 精密整流器详解:原理、选型与调试实战
  • 小信号采样全攻略:从信号调理到ADC选型与噪声抑制
  • AI生成原型工具哪家口碑佳:产品经理选型六大平台深度评测
  • 电压比较器工程实战:迟滞设计、开漏输出与阈值检测全解析
  • Python图形化窗口入门
  • 智能体辅助开发
  • 第一代磁悬浮列车工程解析:悬浮控制与直线电机驱动
  • PB高拍仪集成实战:SDK调用、图像处理与二维码识别
  • STM32H745外扩SDRAM:FMC时序与PCB布线调试全攻略
  • STM32+W5500实现WebSocket客户端:嵌入式实时双向通信实战
  • STM32G431外部时钟配置:从HSE到170MHz主频的完整指南
  • 【Linux】rpm和yum包管理
  • 虚拟机热迁移技术:预拷贝、后拷贝与增量迁移
  • OceanBase分布式数据库核心概念与本地部署实战指南
  • TS 7.0 弃用 emitDecoratorMetadata?Rfclt 运行时类型元数据迁移指南
  • 大模型重塑创作流程:从生产者到判断者的工程实践