ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Linux之线程(一)

Linux之线程(一) 适合零基础入门也可用于期末、考研复盘全文整合实操代码、命令实验与底层原理知识点完整无遗漏。前言当初学操作系统线程和虚拟内存分页时很容易混淆进程与线程、搞不懂 32 位地址分段、CR3 寄存器作用这些高频考点。本文结合代码实操、服务器命令验证把两大块内容完整梳理逻辑循序渐进没有晦涩的跳跃知识点同时预留截图插入位置方便自己复习查阅。第一部分 Linux 线程轻量级进程 LWP1 进程、线程基础概念1.1 什么是进程我们把正在运行的程序称作进程它由内核维护的管理结构、程序代码、全局数据共同构成操作系统是以进程为单位分配硬件资源。 当系统创建新进程时会一次性分配全套独立资源专属虚拟内存空间、独立页目录、私有的文件描述符表、单独的信号处理规则还有专属工作目录与权限掩码。 进程切换的成本很高切换时需要替换页目录刷新 CPU 内 TLB 缓存消耗不少算力。进程是运行起来的程序,内核数据结构代码和数据进程---承担分配相同资源的的基本实体1.2 什么是线程线程是进程内部独立的执行分支一个进程最少有一条主线程也能创建多条子线程。平时直接运行的程序本质就是单线程进程。 这里是 Linux 和其他系统最大的区别内核没有专门存放线程信息的 TCB 结构体Linux 里线程就是轻量级进程 LWP。不管是进程还是线程内核都用task_struct统一管理CPU 调度程序不会区分二者只依靠 LWP 识别不同执行流。线程是进程内部的执行分支必须学习具体的操作通信的线程实现线程资源分为共享、私有两类同进程下所有线程共享资源 整个进程的虚拟地址空间代码段、全局数据、堆、动态链接库、页目录和页表、打开的文件描述符、信号处理函数、文件系统配置工作目录、umask全部共享。每条线程独有的资源 独立栈空间、运行时寄存器上下文、错误标记 errno、调度相关数据以及独属于自己的内核标识 LWP。1.3 进程与线程简单对比对比维度普通进程Linux 线程LWP资源隔离全部资源独立绝大多数资源共享切换时修改 CR3需要修改不用修改唯一标识PIDLWP同进程 PID 完全相同原理Linux的线程原理2 创建线程的 pthread_create 函数详解2.1 头文件与完整函数原型pthread_create参数pthread_t *restrict thread输出型参数传入pthread_t变量地址函数执行成功后会把新建线程的 TID线程 IDTCB 标识写入该变量。restrict是 C 关键字标记该指针是访问对应内存的唯一途径编译器优化用写代码时直接传普通指针即可。const pthread_attr_t *restrict attr线程属性结构体用来设置栈大小、分离属性、调度优先级等 传nullptr/NULL代表使用系统默认线程属性。void *(*start_routine)(void *)— 线程入口函数重点函数指针规定线程主函数固定格式参数void*通用指针返回值void*你之前代码报错根源就是线程函数返回值写成void不匹配该签名。void *restrict arg传给线程入口函数的唯一参数无参数时填nullptr 可传递数字、结构体指针等任意数据在线程函数内强转类型使用。返回值返回0线程创建成功返回非 0 数字创建失败对应错误码如内存不足、属性非法我们发现这两个死循环居然同时在运行,也就是说明这是两个执行流,我们不妨可以进一步验证我们让这两个循环都打印自己的pid,发现pid都是一致的,说明这两个执行流属于同一个进程查看线程LWP 轻量级进程ps -aLPID LWP TTY TIME CMD 2664911 2664911 pts/0 00:00:00 testThread 2664911 2664912 pts/0 00:00:00 testThread字段解释PID 进程 ID两行 PID 完全一致2664911代表两个线程隶属于同一个进程LWP 轻量级进程 ID内核线程 IDLWP2664911主线程 main主线程 LWP 号和进程 PID 相等LWP2664912pthread_create创建的子线程 Linux 内核不区分进程与用户态线程全部统一用 task_struct 管理用户线程在内核层面就是轻量级进程 LWP。区分执行流严格意义上来说,并不是依靠pid而是lwp那么如何杀掉这个呢----直接选择杀掉进程kill -9 pid虚拟地址空间第四讲--页表,虚拟地址 物理地址1 物理内存基础管理规则1.1 物理内存最小单元4KB 页框内核会把整块物理内存切割成大小统一的页框单块页框固定 4KB换算公式4KB2^12。内存和磁盘交换数据时读写的最小单位同样是 4KB。1.2 页框管理结构体 struct page机器上每一块物理页框都会对应一个 struct page 结构体。结构体内部 flags 采用位图存储页面状态一位标记一种状态包含是否空闲、是否脏页、是否锁定、文件缓存、交换分区标记等。使用位图能极大节省内核内存开销。1.3 全局数组 mem [] 统一管理所有页框系统全部物理页框由全局数组 mem [] 统一索引。32 位 4GB 内存设备总页框数量4GB / 4KB 1048576。物理地址与数组下标换算公式物理地址转数组下标index 物理地址 / 4096数组下标转回页框起始地址页基址 index * 4096同一页框里任意物理地址最终都会映射到同一个 struct page页面状态统一。整体逻辑流程物理内存被切分为固定大小页框4KB 标准物理块每个页框状态、用途不同操作系统必须为每一块单独描述、管理。使用struct page结构体作为页框描述符记录单个物理页框所有信息。所有物理页框的struct page统一存放在全局数组mem[]中集中管理。数组下标和物理页框物理地址可以快速互相换算实现地址与描述符一一映射。2 32 位程序虚拟地址空间布局32 位系统完整虚拟地址空间共 4GB分为用户空间、内核空间两大区域用户空间0 ~ 0xC0000000合计 3GB 地址由低到高依次存放程序代码段、初始化全局数据、未初始化数据、堆地址向上增长、共享库映射区域、栈地址向下增长。内核空间0xC0000000 ~ 4GB合计 1GB 所有进程共用同一份内核空间存放内核代码、硬件驱动、全局页表、内核栈等核心资源。3 32 位二级分页虚拟地址 101012 分段规则32 位虚拟地址一共 32 个 bit硬件 MMU 会自动拆分三段1蓝色段高 10 位页目录索引长度10bit取值范围 \(0 \sim 2^{10}-11023\)共 1024 个下标对应结构页目录每个进程独立一张固定 4KB 物理内存作用以 CR3 存放的页目录物理地址为基址用这 10 位找到对应页目录项页目录项内容保存一张页表的物理起始地址。2绿色段中间 10 位页表索引长度10bit同样 0~1023单张页表 1024 个页表项对应结构页表按需分配进程没用到的虚拟区间不会创建页表作用拿到页表物理地址后用这 10 位找到页表项 PTE页表项核心存放物理页框的起始地址同时附带权限位读 / 写 / 执行、存在位、脏位。3红色段低 12 位页内偏移长度12bit\(2^{12}4096\)正好对应 4KB 一页关键特性不参与页表查找作用找到物理页框首地址后直接加上这 12 位偏移定位页内具体的字节举例物理页框起始地址 0x1000偏移 0x0FF → 最终物理地址 0x10FF。每张页表同样包含 1024 个页表项 (PTE)单张页表 4KB 一个页目录项对应一张独立页表图里画出 3 张页表代表不同虚拟区间。一条虚拟地址完整翻译流程CPU 执行代码输出 32 位虚拟地址送入 MMUMMU 拆分虚拟地址高 10 位、中间 10 位、低 12 位从 CR3 寄存器读取当前进程页目录物理首地址高 10 位索引页目录取出对应页表的物理地址中间 10 位索引页表项拿到物理页框首地址物理页框地址 低 12 位页内偏移 最终物理内存地址硬件访问物理内存若页表项 “存在位 0”代表该虚拟地址对应的页面在磁盘交换区触发缺页异常内核加载页面到物理内存后重试。页表大小:4MB4kb单个进程不可能拥有全部内存,当前进程页表总数远远小于4MB懒加载,写实拷贝,缺页中断---页表少--页表会更少写时拷贝会讲整个4kb进行拷贝-----局部性原理(磁盘上的数据可以提前加载到内存)任何一个页框的地址,用多少位就够了页框的起始地址低12位都是0----用20比特位表示页框的起始地址,对地址进行相应的右移12位,左移12位来完成---20位表示页表的地址剩下来12位---标志位,以上工作由硬件完成mmu,因为效率高4 CR3 寄存器核心知识点4.1 CR3 寄存器存储内容CR3 是 CPU 专属硬件寄存器里面存放的是当前进程页目录的物理起始地址绝对不能存放虚拟地址。 原因很容易理解翻译虚拟地址需要依赖页目录如果 CR3 存虚拟地址想要读取页目录又要翻译地址会形成无限循环。4.2 CR3 和进程、线程切换的关联硬件层面整颗 CPU 只存在一个 CR3 寄存器内核层面每个进程的 task_struct 结构体都会保存自身页目录的物理基地址进程切换完整流程 ① 保存旧进程把当前 CR3 的值存入旧进程的 task_struct ② 加载新进程读取新进程存储的页目录物理地址写入 CR3 ③ CR3 更新完成后MMU 地址翻译规则同步切换实现进程地址空间隔离。线程切换特殊点同一个进程所有线程共用一套页目录切换线程不用修改 CR3这也是线程切换开销远小于进程的关键原因。5 MMU 完成虚实地址转换完整流程CPU 执行指令输出 32 位虚拟地址送入内存管理单元 MMUMMU 读取 CR3 寄存器拿到当前进程页目录的物理基地址截取虚拟地址高 10 位作为索引查询页目录得到对应页表物理地址截取虚拟地址中间 10 位作为索引读取页表内部 PTE 页表项判断 PTE 有效标记位标记有效提取物理页框基地址拼接低 12 位偏移访问物理内存标记无效触发缺页异常内核分配物理页、从磁盘加载对应数据、更新页表项之后重新执行出错指令。拥有更多的虚拟地址---拥有更多的内存,只要我们将虚拟地址进行划分,本质就是划分物理内存页表的本质:是进程看到内存资源的窗口,拥有更多的虚拟地址,拥有更多的内存,划分区域,划分虚拟地址,在编译阶段进行划分.linux在物理上,存在真正意义上的线程吗??---不存在,没有TCB,只存在轻量级进程进程拥有独立性大部分资源独立线程拥有共享性,大部分资源共享总结CR3 寄存器只存放页目录物理地址切换进程需要修改 CR3同进程切换线程无需修改PID 区分不同进程LWP 区分同进程内多条线程多线程程序所有线程 PID 完全相同clone 依靠 flags 标识控制资源共享CLONE_VM 是进程和线程的分界线fork 是 clone 的特殊实现32 位二级分页虚拟地址分段固定 101012低 12 位对应 4KB 页框偏移struct page 管理单块物理页框全局 mem [] 数组索引整机所有物理内存Linux 没有独立线程内核结构线程就是轻量级进程 LWP进程、线程统一由 task_struct 调度pthread 库只是用户层封装底层创建线程依赖 clone 系统调用。
返回列表