线程的理解

线程理解

一、 进程与线程的本质区别

  • 进程是资源分配的最小单位
    • 内核为每个进程分配独立的虚拟地址空间(页表)、文件描述符表、信号处理规则等。
    • 关键点:进程之间是"老死不相往来"的,内存隔离是默认状态。
  • 线程是CPU调度的最小单位
    • 线程是寄生在进程里的执行流。它没有独立的地址空间,只拥有维持执行所必需的最少资源:栈(Stack)、寄存器上下文(Context)、线程ID(TID)
    • 关键点:同一进程内的线程共享该进程的"家底"(内存、文件句柄),这使得它们之间的通信成本几乎为零,但也带来了数据竞争的风险。

二、 共享资源的底层透视(重点)

1. 代码段与数据段(Text & Data Segment)

  • 底层实现 :所有线程共用同一套页表映射。这意味着,如果线程A修改了全局变量 global_count,线程B在下一次读取时,看到的是修改后的值(前提是缓存一致性协议生效)。
  • 隐患
    • 只读安全 :代码段是只读的,所以多线程调用同一个函数(如 printf)是安全的,不需要加锁。
    • 读写危险:全局变量、静态变量位于数据段或BSS段,是所有线程的"公共厕所",必须加锁(Mutex)或使用原子操作(Atomic)保护。

2. 文件描述符表(File Descriptor Table)

  • 底层实现 :这是内核维护的一张表,记录了进程打开了哪些文件/Socket。线程共享这张表,意味着它们共享文件的偏移量(Offset)
  • 隐患
    • 并发读写冲突:如果线程A和线程B同时向同一个文件描述符写数据,且没有加锁,数据会交错混乱。
    • 生命周期管理 :线程A关闭了某个Socket(close(fd)),线程B如果此时还在尝试读写这个fd,会直接报错 EBADF。这是多线程网络编程中常见的Bug。

3. 信号处理(Signal Handling)

这是一个极易混淆的点,必须区分处理方式屏蔽字

  • 共享部分(处理方式)SIG_IGN(忽略)、SIG_DFL(默认)或自定义的处理函数(Handler)。这些是注册在进程控制块(PCB)里的。如果你在线程A里把 SIGINT 设为忽略,整个进程的所有线程都会忽略它。
  • 私有部分(屏蔽字 Mask):每个线程可以决定自己"暂时不想收哪些信号"。这通常用于关键代码段的保护。
  • 发送目标 :发给进程的信号(如 kill -9 pid),内核会随机挑选一个未屏蔽该信号的线程来执行处理函数。

4. 其他环境信息

  • 当前工作目录(CWD) :进程级共享。线程A调用 chdir("/tmp"),线程B紧接着打开相对路径文件 ./data.txt,实际上打开的是 /tmp/data.txt。这在复杂的多线程程序中是一个巨大的隐形炸弹。
  • 用户ID/组ID :决定了进程的权限。一旦改变(如 setuid),所有线程的权限都会瞬间改变。

三、 线程私有成员

为了保证多线程能独立运行而不互相干扰,每个线程必须有自己的一套"私有成员

  1. 栈空间(Stack)
    • 这是线程最核心的私有资源。函数调用的局部变量、返回地址都存在这里。
    • 注意:栈溢出(Stack Overflow)通常只会搞挂当前线程,而不会直接搞挂整个进程(除非破坏了关键的共享数据结构)。
  2. 寄存器上下文(Register Context)
    • 包括程序计数器(PC,指向下一条指令)、栈指针(SP)、通用寄存器等。
    • 原理:当操作系统进行线程切换时,本质上就是把当前线程的寄存器值保存到内存(TCB中),再把下一个线程的寄存器值加载到CPU。这就是"上下文切换"。
  3. errno
    • C语言标准库的全局变量 errno 在多线程环境下必须是线程私有的(通过 TLS 线程局部存储实现)。否则,线程A刚发生错误设置了 errno,还没等读取,就被线程B的成功操作覆盖了,导致错误诊断完全失效。
  4. 信号屏蔽字(Signal Mask)
    • 允许线程根据自己的执行阶段,动态地屏蔽某些信号,防止被打断。

四、 四种进程/线程模型的

结合你提供的图片,我们来看这四种形态在实际工程中的取舍:

模型 核心特征 适用场景 致命弱点
单线程进程 简单、无锁、串行 简单的脚本、嵌入式控制逻辑、早期的 DOS 程序 I/O 阻塞即死机;无法利用多核 CPU。
多进程(多个单线程) 隔离性最强、稳定性高 Web 服务器的主控进程(如 Nginx Master)、安全性要求极高的系统 通信成本高(IPC);创建/切换开销大(需换页表)。
多线程(单进程多线程) 切换极快、通信零成本 高并发服务器(Tomcat, Go Runtime)、GUI 应用 "一损俱损"(一个线程野指针可能搞挂整个进程);调试困难。
多线程进程组 兼顾隔离与并发 现代浏览器(Chrome 每个标签页一个进程,进程内多线程)、大型数据库 架构极其复杂;资源消耗大。

线程是操作系统为了在"资源共享"和"独立执行"之间寻找平衡而创造的产物。

相关推荐
Huangjin007_2 小时前
【Linux 系统篇(十五)】进程 (三) :进程状态深度详解
linux·运维
RisunJan2 小时前
Linux命令-talk(终端实时对话)
linux·运维·服务器
NeilYuen2 小时前
【vemory】高性能KV存储AOF方案设计
linux·c++·redis·缓存
三言老师3 小时前
Rocky Linux 8.6 整机系统备份与迁移方案文档文档用途
linux·运维·服务器·网络
雾时之林3 小时前
Linux----防火墙
linux·运维·网络
我星期八休息5 小时前
网络编程—NAT、代理服务与内网穿透
linux·服务器·网络·网络协议
RisunJan5 小时前
Linux命令-sysctl(运行时修改内核参数)
linux·网络·智能路由器
yunwei375 小时前
eBPF 教程:使用 fsession 追踪慢速 vfs_read 调用
linux·云原生
A_humble_scholar5 小时前
Linux 网络基础(三)下的 HTTP 核心详解:从请求响应到状态管
linux·网络·http