Linux线程深度剖析:轻量级进程、虚拟内存分页、进程线程资源对比

Linux线程深度剖析:轻量级进程、虚拟内存分页、进程线程资源对比

    • 前言
    • 一、到底什么是Linux线程?
      • [1.1 感性理解:进程与线程的通俗模型](#1.1 感性理解:进程与线程的通俗模型)
      • [1.2 Linux独特实现:线程就是轻量级进程LWP](#1.2 Linux独特实现:线程就是轻量级进程LWP)
    • 二、理解线程的底层基础:虚拟地址空间与分页内存管理
      • [2.1 如果没有分页机制:物理内存碎片问题](#2.1 如果没有分页机制:物理内存碎片问题)
      • [2.2 页、页框,struct page物理页管理](#2.2 页、页框,struct page物理页管理)
      • [2.3 单级页表的实现与致命缺陷](#2.3 单级页表的实现与致命缺陷)
      • [2.4 二级页表的设计,CR3寄存器作用](#2.4 二级页表的设计,CR3寄存器作用)
      • [2.5 32位二级页表完整地址转换全过程](#2.5 32位二级页表完整地址转换全过程)
      • [2.6 MMU硬件单元与TLB快表,解决多级页表查询性能问题](#2.6 MMU硬件单元与TLB快表,解决多级页表查询性能问题)
      • [2.7 缺页异常Page Fault](#2.7 缺页异常Page Fault)
        • [2.7.1 硬缺页 Major Page Fault](#2.7.1 硬缺页 Major Page Fault)
        • [2.7.2 软缺页 Minor Page Fault](#2.7.2 软缺页 Minor Page Fault)
        • [2.7.3 无效缺页 Invalid Page Fault](#2.7.3 无效缺页 Invalid Page Fault)
      • 2.8【面试思考拓展】结合malloc、写时拷贝理解缺页
    • 三、线程的优缺点、异常行为以及适用场景
      • [3.1 线程的优点](#3.1 线程的优点)
      • [3.2 线程的缺点](#3.2 线程的缺点)
      • [3.3 ⚠️线程异常:一个线程崩溃,整个进程直接退出](#3.3 ⚠️线程异常:一个线程崩溃,整个进程直接退出)
      • [3.4 线程适用场景深度解析](#3.4 线程适用场景深度解析)
    • 四、进程与线程对比:哪些资源共享,哪些是线程私有?
      • [4.1 核心概念回顾](#4.1 核心概念回顾)
      • [4.2 线程独占(私有)资源](#4.2 线程独占(私有)资源)
      • [4.3 所有线程共享的进程级资源](#4.3 所有线程共享的进程级资源)
      • 4.4【面试辨析】什么叫单线程进程?
    • 六、全文面试考点总结

前言

很多后端开发者写多线程代码,熟练调用pthread_create、pthread_join,却不理解线程在内核到底是什么。面试经常被问到:线程和进程内核层面的区别?为什么线程切换比进程切换快?缺页异常和段错误是什么关系?子线程崩溃会不会导致整个进程退出?

很多教材会直接抛出定义:进程是资源分配基本单位,线程是调度基本单位 ,但很少讲透背后本质。Linux并没有内核原生TCB线程控制块,线程是基于轻量级进程LWP模拟实现 ,资源共享本质就是虚拟地址空间、页表的共享。

如果不理解虚拟地址空间、分页内存、轻量级进程LWP这些底层原理,很多线程相关的面试题只能死记硬背,遇到变形问题就容易出错。读完本文,你可以彻底搞懂:Linux线程内核本质、分页虚拟内存完整工作流程、进程与线程资源划分逻辑,从容应对操作系统线程相关面试。

一、到底什么是Linux线程?

1.1 感性理解:进程与线程的通俗模型

教材经典定义:

  • 进程 :内核数据结构 + 代码数据,承担分配系统资源的基本实体。
  • 线程 :进程内部的执行分支(执行流),CPU调度的基本单位,线程在进程内部运行。

💡生活化类比:把操作系统资源分配类比社会家庭。

进程 = 家庭 ,家庭是分配社会资源的基本实体;线程 = 家庭里面的每一个人 。

全家人共享房子、家具(进程全部资源);每个人拥有自己私人笔记本、私人物品(线程私有资源);每个人可以独立干活、做不同事情。

  • 进程强调独占资源 ;线程强调共享资源,同时保留自己的私有上下文。
    📌核心朴素结论(面试口述非常好用)
  1. Linux内核没有传统操作系统意义上真正的线程,线程由轻量级进程LWP模拟实现。
  2. Linux可以通过进程(LWP)模拟线程,资源划分本质就是对虚拟地址空间、页表的划分;虚拟地址本身就是资源的代表。
  3. CPU眼里看到的调度任务永远是task_struct(PCB),CPU不区分这是进程还是线程。
  4. 单线程进程:只是进程内部只有一条执行流的特殊情况,main主线程就是这个唯一的执行流。

❓思考问题

  1. Linux为什么要采用LWP模拟线程这种设计?
  2. Windows内核是怎么做线程?和Linux实现有什么差异?

Windows内核会区分进程对象EPROCESS和线程对象ETHREAD,内核原生维护TCB线程控制块;而Linux选择复用task_struct,依靠clone系统调用flag标记实现资源共享/拷贝,内核不需要维护两套调度逻辑,实现更简洁。

易错辨析:

❌错误认知:LWP就是用户态pthread_t的ID。

✅正确:pthread_t是用户态pthread库层面标识,本质用户空间内存地址;LWP是内核task_struct的id,由内核维护,二者完全不是同一个东西。

1.2 Linux独特实现:线程就是轻量级进程LWP

很多操作系统在内核专门设计线程控制块TCB,区分进程PCB和线程TCB。但是Linux内核没有专门为线程设计独立结构体。

Linux内核中,进程和线程全部复用同一个结构体task_struct(PCB) 。CPU调度器看到的,全部都是task_struct对象。

  • 传统独立进程:每个task_struct绑定独立的mm_struct(虚拟地址空间描述符)
  • 多线程:同一个进程内多个task_struct,全部指向同一个mm_struct,共享整套虚拟地址空间,这种task_struct叫做LWP轻量级进程。

Linux进程、多个task_struct线程共用虚拟地址空间示意图

图片内容说明:图中展示task_struct、mm_struct、虚拟地址空间、物理内存的关系;单进程多线程场景下多个task_struct共用同一个mm_struct,主线程与子线程的内核结构对比。

用户层的pthread库,只是对内核clone系统调用做封装。调用pthread_create,底层调用clone传入特殊标志位,创建共享地址空间的轻量级进程。

主线程同样也是LWP,主线程的LWP ID和进程PID是相等的,使用ps -aL命令可以观察进程内全部LWP。

二、理解线程的底层基础:虚拟地址空间与分页内存管理

说明:要真正懂线程资源划分,必须搞懂虚拟内存分页机制。

✨关键金句:资源划分本质就是地址空间划分;资源共享本质就是虚拟地址的共享,也就是页表条目的共享。线程划分资源,本质就是划分合法虚拟地址范围,操作页表映射。

2.1 如果没有分页机制:物理内存碎片问题

假设操作系统没有虚拟内存、没有分页机制。程序直接访问物理内存,每个程序必须分配一段连续的物理内存 存放代码段、数据段、栈段。

当程序不断创建、退出,内存会不断被分割成大小不一的空闲小块。随着时间推移,会出现大量外部碎片:内存总空闲大小足够,但是没有一块连续物理内存可以满足新程序的内存申请需求,内存明明空闲却分配失败。

无分页机制物理内存分配碎片、4KB页框管理示意图

图片内容说明:物理内存分页、struct page,虚拟地址到物理内存映射的基础图示。

我们的理想目标:给进程提供连续的逻辑虚拟地址;物理内存可以使用离散、不连续的物理块。分页存储管理就用来解决这个矛盾。

2.2 页、页框,struct page物理页管理

  • 页(Page):逻辑层面,虚拟内存被切分成固定大小的数据块,称为页。
  • 页框(Page Frame):物理内存被切分成同样大小的存储区域,称为页框。

Linux默认页大小4KB。32位4G虚拟地址空间,总页数:4GB / 4KB = 1048576。

💡申请物理内存到底在干什么?

内核维护数组struct page mem[1048576],数组每一项对应一个物理页框。申请物理内存,就是在这个数组里面找到空闲的struct page,拿到数组下标index。

最终物理页起始地址 = index * 4KB;再配合页内偏移,得到完整物理地址。

👉不需要在struct page内部保存物理地址,数组下标就等价物理页编号。

Linux内核中,每一个物理页框,都对应一个struct page结构体,用来描述物理页的状态。不需要完整背诵结构体源码,重点理解3个核心成员:

  1. flags:一组标志位,每一个bit代表物理页状态。比如PG_locked表示页面被锁定,PG_dirty表示脏页(内存修改还没刷回磁盘),PG_uptodate代表页面数据有效。
  2. _mapcount:引用计数。记录有多少个页表项映射到这个物理页。-1表示该物理页没有任何映射,可以分配使用。写时拷贝机制高度依赖这个计数。
  3. virtual:该物理页对应的内核虚拟地址;高端内存页面不会永久映射,该字段为NULL,需要动态映射。

简单计算内存开销:4GB物理内存,4KB一页,总共有1048576个物理页。假设每个struct page占40字节,全部结构体只占用约40MB内存。开销很小,内核完全可以承受。

【面试思考】页的大小是不是越大越好?

页太大:页内碎片严重 。程序只需要几字节,也要占用完整4KB页,内存浪费。

页太小:页数量暴增,页表占用内存急剧膨胀;MMU页表遍历次数变多,CPU开销上升。

所以操作系统选择适中的4KB作为默认页大小。

2.3 单级页表的实现与致命缺陷

32位系统虚拟地址空间大小是4GB。如果采用单级页表,每4KB虚拟页对应一个页表项。总表项数量 = 4GB / 4KB = 1048576。每个页表项占4字节,单级页表总大小4MB。

重要:Linux不会使用简单单级页表,单级页表存在硬伤。

  1. 单级页表要求页表本身必须存放在连续物理内存。当初引入分页就是为了解决连续内存的问题,页表自己却需要连续大块内存,矛盾。
  2. 按照程序局部性原理,一个进程往往只使用很小一部分虚拟地址。比如一个程序只用10MB内存,单级页表仍然要完整占用4MB,大量页表项闲置,造成内存浪费。

为了解决单级页表的缺陷,Linux使用二级页表(32位x86),把页表进行分层。

32位系统单级页表映射、多级页表整体结构图

图片内容说明:task_struct/mm_struct,虚拟地址空间分段(内核区、栈、共享区、堆、数据段、代码段),物理内存映射,二级页表整体框架。

2.4 二级页表的设计,CR3寄存器作用

二级页表分为两层:

  1. 页目录(PGD):顶层,每一项指向一个二级页表。
  2. 页表(PTE):底层,每一项指向真实物理页框。

CR3寄存器是CPU硬件寄存器,保存当前正在运行进程的页目录的物理地址,属于进程硬件上下文。进程发生切换的时候,操作系统会修改CR3寄存器的值,指向新进程的页目录。

重点!线程切换的时候,不会修改CR3寄存器,因为多线程共享同一个页目录;进程切换必须修改CR3。这就是线程切换开销低的关键根源。

CR3寄存器+二级页目录、二级页表地址拆分示意图

图片内容说明:32位虚拟地址32bit拆分(10+10+12),MMU、CR3,页目录、页表、页内偏移,虚拟地址翻译完整流程。

二级页表的好处:不需要一次性分配全部页表。进程没有使用的虚拟地址,对应的页表可以根本不创建。比如程序只需要12MB内存,仅仅需要3个底层页表,而不是完整1024个页表,极大节省内存。

2.5 32位二级页表完整地址转换全过程

32位,4KB页面,虚拟地址一共32bit,做如下拆分:

  • 高10bit:一级页号(页目录索引)
  • 中间10bit:二级页号(页表索引)
  • 低12bit:页内偏移,虚拟页内部的偏移量,和物理页偏移完全一致,范围0~4095。

MMU硬件完成完整地址转换步骤:

  1. 读取CR3寄存器,拿到当前进程页目录的物理起始地址。
  2. 使用虚拟地址高10bit,作为索引,查询页目录,获取对应底层页表的物理地址。
  3. 使用虚拟地址中间10bit,作为索引,查询底层页表,得到目标物理页框的物理基地址。
  4. 将物理页框基地址,拼接虚拟地址低12位页内偏移,得到最终完整物理地址。

注意:物理页框的起始地址一定是4KB对齐,低12bit全部为0。页表项中只需要保存物理页高20位即可,剩下bit存放权限位(读、写、用户/内核权限)。
💡拓展理解:写时拷贝、缺页异常、内存申请,背后都在操作页表,重新建立虚拟地址到物理页框的映射关系。

【面试易错辨析】

二级页表访问一次内存,实际上需要三次内存访问:1次访问页目录,1次访问页表,1次访问真正的数据。如果是四级页表,内存访问次数更多。多级页表节省内存,但是增加地址转换的时间开销。

2.6 MMU硬件单元与TLB快表,解决多级页表查询性能问题

MMU(Memory Manage Unit,内存管理单元),是CPU内部硬件电路,专门负责虚拟地址到物理地址转换,同时做内存权限检查。所有用户程序访问内存,必须经过MMU转换。

多级页表每一次内存访问都要多次读取内存,性能很差。于是引入TLB(Translation Lookaside Buffer,转译后备缓冲器,俗称快表) 。

TLB是CPU内部高速缓存,专门缓存「虚拟页号→物理页框」映射条目。

完整访问流程:

  1. CPU发出虚拟地址,交给MMU。
  2. MMU优先查询TLB缓存:
    • TLB‑Hit(命中):缓存中存在该虚拟页映射,直接取出物理页框,拼接偏移得到物理地址,直接访问内存,不需要遍历多级页表,速度极快。
    • TLB‑Miss(缺失):缓存没有该映射。MMU去内存遍历多级页表完成地址翻译;同时把这条映射存入TLB缓存,供后续访问复用。

重点面试考点:

  1. 进程切换:修改CR3寄存器,TLB整体刷新失效。旧进程全部缓存的映射全部作废,新进程访问内存大量TLB‑miss,带来很大性能开销。
  2. 线程切换:同一个进程内部线程切换,CR3寄存器不改变,TLB缓存全部保留有效,不需要刷新。这就是线程切换比进程切换快的关键原因之一。
    补充:除TLB之外,CPU的L1/L2/L3数据缓存也会在线程切换时部分保留;进程切换会大量污染缓存。二者叠加,放大进程切换的性能损耗。

2.7 缺页异常Page Fault

MMU查询页表的时候,如果发现页表项present有效位为0,虚拟地址没有合法的物理内存映射,硬件触发缺页异常(Page Fault) 。CPU从用户态切换到内核态,调用内核Page Fault Handler缺页异常处理函数,根据不同情况做不同处理。

2.7.1 硬缺页 Major Page Fault

物理内存中完全没有该页数据,需要从磁盘读取数据加载到物理内存 。

典型场景:访问磁盘映射文件、页面被swap交换到磁盘。

开销很高,涉及磁盘IO,毫秒级别,会明显拖慢程序性能。

2.7.2 软缺页 Minor Page Fault

页面的数据已经存在物理内存,只是页表还没有建立映射,不需要磁盘IO 。

典型场景:

  1. malloc分配内存之后第一次访问(惰性分配,虚拟地址已经分配,物理页还没分配,第一次访问触发软缺页分配物理页)。
  2. fork之后写时拷贝,父子进程共享物理页,发生写操作触发软缺页复制页面。
  3. 多进程共享内存,新进程第一次访问共享区域。>

开销小,只需要内存操作,微秒级别。

2.7.3 无效缺页 Invalid Page Fault

访问的虚拟地址根本不在进程合法虚拟地址区间;或者权限不匹配,比如只读页面执行写操作。内核直接向进程发送SIGSEGV信号,也就是我们熟悉的段错误Segment Fault,进程直接终止。

【面试高频辨析】缺页异常≠段错误

软/硬缺页属于正常机制,内核修复之后程序继续运行;无效缺页才会触发段错误,杀死进程。
✨笔记补充重要考点:数组越界不一定会崩溃!

如果越界访问刚好落在进程已经合法映射的虚拟页内,操作系统不会捕获,只会默默篡改内存,产生隐蔽bug。只有访问不在合法虚拟地址区间,触发无效缺页,才会段错误崩溃。

2.8【面试思考拓展】结合malloc、写时拷贝理解缺页

很多同学疑惑:malloc(10*1024*1024*1024)申请10GB内存,返回成功,但是top看物理内存占用几乎不上涨?

这就是Linux的惰性内存分配 :malloc/new仅仅在虚拟地址空间划分出一块合法虚拟区间,不会立刻分配物理内存。只有真正读写这块内存的时候,触发缺页异常,内核才分配物理页框。

写时拷贝(COW)也是软缺页典型场景:fork之后父子进程共享全部物理页,页表标记只读。当任意一方修改内存,触发软缺页,内核复制一份新物理页给修改方。

三、线程的优缺点、异常行为以及适用场景

3.1 线程的优点

  1. 创建销毁代价更小
    创建线程,内核只需要新建一个task_struct,分配线程私有栈,复用进程已经存在的mm_struct、页表、文件描述符等全部资源。
    创建进程需要完整拷贝(写时拷贝)虚拟地址空间、页表,资源开销远大于线程。
  2. 线程上下文切换开销远小于进程切换

理解:上下文切换最大损耗是寄存器保存恢复;隐藏损耗是扰乱CPU缓存。进程切换更换页表,TLB全部作废;线程切换共享虚拟地址,TLB不刷新。

  • 进程切换:保存寄存器;修改CR3寄存器切换页目录;TLB全部刷新失效;CPU缓存大量失效。直接、间接开销都很大。
  • 线程切换:仅仅切换寄存器上下文,栈指针;CR3不变,虚拟地址空间不变,TLB缓存完全保留,不需要刷新。切换的直接开销小,缓存间接开销几乎没有。>

注意:线程切换同样有开销,寄存器保存恢复、内核调度逻辑还是要消耗CPU,只是相比进程切换轻量很多。

  1. 占用系统资源更少
    线程只需要task_struct + 私有栈,不需要独立虚拟地址空间,内存资源消耗远小于进程。
  2. 充分利用多CPU多核并行计算
    多线程可以调度到不同CPU核心真正并行执行,把CPU算力充分发挥。进程当然也可以,但是进程资源开销更大。
  3. 计算与IO等待可以互相重叠执行
    一个线程阻塞等待慢速IO(磁盘、网络),CPU可以调度其他线程执行计算逻辑,不用干等,提升整体吞吐。

3.2 线程的缺点

  1. 性能损失
    如果CPU密集型线程数量远超CPU核心数,大量时间消耗在线程调度切换,大量上下文抢占,反而会降低整体性能,增加同步互斥锁开销。
  2. 健壮性降低,线程缺少隔离保护
    同一个进程的所有线程共享地址空间。一个线程写越界,破坏全局堆、全局变量,直接影响其他全部线程。进程之间有严格地址空间隔离,一个进程崩溃不会干扰其他进程。
  3. 缺少访问控制粒度
    进程是操作系统访问控制的基本单位。很多系统调用作用于整个进程,比如信号处理、工作目录、用户ID。线程调用部分系统调用会影响全部线程,而不是仅仅当前线程。
  4. 编程、调试难度上升
    多线程会遇到竞态条件、死锁、数据竞争,bug复现随机性很强。gdb调试多线程、定位死锁难度远高于单线程程序,引出线程安全的概念。

3.3 ⚠️线程异常:一个线程崩溃,整个进程直接退出

【面试高频易错点】很多人误以为子线程崩溃只会杀死自己,其他线程继续运行。

✅真相:只要进程内部任意一条线程发生异常(除零错误、野指针访问触发段错误),内核发送信号,信号是进程级别的,整个进程收到信号,全部线程一起被销毁退出。

原理:线程异常触发硬件异常,内核生成信号递交给进程。信号处理粒度以进程为单位,只要收到致命信号,操作系统直接回收整个进程的全部资源,进程内所有task_struct(所有线程)全部销毁。主线程崩溃和子线程崩溃效果完全一致。

拓展:能不能做到一个线程崩溃,其他线程继续跑?Linux原生做不到。可以借助子进程、进程隔离方案实现。

3.4 线程适用场景深度解析

1)CPU密集型程序 :大量做计算,CPU满负载。

多线程目的是利用多核CPU真正并行运算。线程数量建议约等于CPU核心数 。

如果线程数量远大于核心,CPU花大量时间做线程切换,性能反而下降。

示例:视频编解码、数值计算、模型推理。

2)IO密集型程序 :大部分时间阻塞等待网络、磁盘IO,CPU经常空闲。

IO阻塞的时候线程让出CPU,其他线程可以继续工作。线程数量可以远大于CPU核心数量。

示例:网络服务器,同时等待大量客户端socket IO。

【面试问答】IO密集型为什么不用多进程?多进程资源开销更大,进程间通信复杂;线程共享地址空间,数据交互简单,资源开销小。

四、进程与线程对比:哪些资源共享,哪些是线程私有?

4.1 核心概念回顾

进程是资源分配的基本单位,线程是CPU调度的基本单位 。

Linux内核视角:进程和线程都是task_struct。区别就是:传统进程拥有独立mm_struct虚拟地址空间;同一进程的多个线程task_struct指向同一个mm_struct。

4.2 线程独占(私有)资源

每个线程自己独有的资源,其他线程不可直接使用。

私有资源 简要说明
线程ID(内核LWP、用户pthread_t) LWP是内核调度标识;pthread_t为用户库层ID
一组寄存器(上下文) 保存线程运行时CPU寄存器,线程切换时保存恢复
线程私有栈 保存函数局部变量、函数调用栈帧;子线程栈由pthread库mmap分配,大小默认8M,不可动态增长;主线程栈由内核管理,可以动态扩展
errno变量 线程局部变量,每个线程独立,避免多线程相互干扰
信号屏蔽字 每个线程可以设置自己的信号掩码,屏蔽特定信号
调度优先级 线程独立的调度优先级属性
线程局部存储TLS pthread_key创建的线程私有全局数据

4.3 所有线程共享的进程级资源

同一个进程内全部线程共享这些资源,任意线程修改,其他线程立刻可见。

共享资源 简要说明
虚拟地址空间(mm_struct) 代码段、全局数据段、堆全部共享,可以访问同一个全局变量,调用同一个函数
文件描述符表 open打开的fd全部共享,一个线程close(fd)其他线程无法再使用该fd
信号处理方式 SIG_IGN、SIG_DFL或者自定义信号处理函数,进程全局生效
当前工作目录cwd chdir修改工作目录,全部线程生效
用户ID、组ID(uid/gid) 用户身份凭证,进程级别
内存映射(mmap) 共享库、共享内存映射全部共享

4.4【面试辨析】什么叫单线程进程?

我们写普通C/C++程序,只有main函数,这就是单线程进程 。

在内核视角:进程只有一个task_struct(主线程LWP),task_struct绑定mm_struct虚拟地址空间。进程=资源容器 + 唯一执行流。

拓展提问思考:主线程栈和子线程栈有什么区别?

  1. 主线程栈:内核分配,进程栈,可以动态向下扩展,栈越界可以自动扩充,超过上限才报段错误。
  2. pthread创建出来的子线程栈:pthread库调用mmap在共享区申请,默认8MB,大小固定,不能动态增长。栈写满直接栈溢出,触发段错误。
复制代码
// 简单示意,主线程和子线程栈来源不同
// 主线程栈:内核在创建进程时设置
// 子线程栈:pthread库内部通过mmap(MAP_ANONYMOUS)申请一块匿名内存作为栈

六、全文面试考点总结

整理高频面试问题与记忆清单,复习快速回顾:

  1. Linux内核线程本质 :没有专门内核TCB,线程就是LWP轻量级进程,复用task_struct;多线程共享同一个mm_struct虚拟地址空间。pthread_create底层封装clone系统调用。

  2. 灵魂面试口述金句:资源划分本质就是虚拟地址空间、页表的划分;资源共享本质就是虚拟地址、页表条目的共享。虚拟地址就是资源的代表。

  3. 进程与线程核心定义:进程是资源分配的基本单位,线程是CPU调度的基本单位。

  4. 线程切换为什么比进程切换快?

    • 线程切换不修改CR3寄存器,不需要更换页目录,TLB不会整体刷新;进程切换修改CR3,TLB全部失效。
    • 线程只切换寄存器上下文;进程切换除寄存器,还要处理地址空间,缓存大量失效,间接开销巨大。
  5. 二级页表、MMU、TLB工作流程:32位虚拟地址拆分10+10+12;CR3存页目录物理地址;TLB缓存虚实映射,减少页表遍历次数。TLB‑hit直接转换,TLB‑miss遍历多级页表。

  6. 三类缺页异常区分

    • 硬缺页Major:需要磁盘IO;
    • 软缺页Minor:内存已有数据,仅建立映射,无磁盘IO(malloc惰性分配、写时拷贝);
    • 无效缺页:非法访问,发送SIGSEGV段错误杀死进程。>

    拓展:数组越界不一定崩溃,落在合法虚拟页只会篡改内存。

  7. 线程异常关键点:进程内任意线程崩溃,整个进程全部退出。信号是进程粒度。

  8. 线程资源表格记忆:哪些私有、哪些共享。子线程栈mmap固定大小,主线程栈可动态扩展。

  9. pthread库底层:Linux内核没有线程系统调用,pthread库封装clone;1:1模型,用户线程对应内核LWP;pthread_t是库内地址,不等于LWP。

  10. 适用场景:CPU密集线程数≈CPU核心数;IO密集线程数可以大于CPU核心。线程过多会带来调度开销,性能下降。

  11. 易错坑点

  • pthread_t(用户层ID)不等于内核LWP ID;
  • malloc成功不代表分配物理内存,惰性分配,访问才触发缺页;
  • 内存越界不一定立刻段错误,如果落在合法页只会破坏内存;
  • 子线程不要调用exit();pthread_exit参数不能传局部栈地址;

补充面试反问点:虚拟内存分页解决的是什么问题?外部碎片;页表多级分页解决单级页表需要连续内存、内存浪费的问题。

对比Windows与Linux线程实现差异。

相关推荐
骑着蜗牛撵大象3272 小时前
穿越 Docker 内核迷雾:镜像分层的叠加态与卷挂载的空间穿梭
运维·docker·容器·联合文件系统·卷挂载·镜像分层·存储驱动
honsor2 小时前
以太网温湿度传感器:RJ45直连机房的环境监控新方案
运维·网络·数据库·物联网·安全·云计算·github
zxcvb1532 小时前
规定备份信息的备份方式:从策略制定到自动化落地的实践思考
运维·oracle·自动化
ZeroNews内网穿透3 小时前
内网穿透安全加固实践:通过 Geo 地理围栏阻挡境外扫描风险
运维·安全·devops
柏慧通云报餐3 小时前
智慧食堂食材溯源数字化方案|采购 - 入库 - 餐桌全链路记录体系落地实践
大数据·运维·人工智能
北风催呀催3 小时前
HuggingFace下载镜像加速技巧
linux·huggingface
河北清兮网络科技3 小时前
直播APP商用开发深度解析:为什么模板系统无法支撑规模化直播平台
运维·网络·人工智能·小程序·短剧app
MicrosoftCloud3 小时前
性能排查 02|vmstat、iostat、mpstat、sar 怎么读?性能四件套的分工与判读阈值
linux·ubuntu·vmstat·性能监控·iostat·sysstat