Linux基础:进程概念

📅 2026/8/12 16:54:49
Linux基础:进程概念
文章目录 吐血整理小白也能秒懂的 Linux 进程概念大揭秘硬核详细版1. 祖师爷的宝训冯·诺依曼体系结构1.1 硬件的“权力游戏”2. 计算机里的大管家操作系统 (OS)2.1 操作系统是个啥2.2 管理的六字真言先描述再组织3. 揭开进程的神秘面纱3.1 到底什么是进程3.2 进程的“户口本”PCB3.2.1 cwd 和execwd 与 exe 核心对比表3.3 影分身之术fork()3.4 进程的“七情六欲”状态流转3.5 让人头疼的“僵尸”与“孤儿”3.6 谁先吃肉进程调度与 O(1) 算法3.7 高频误区汇总、bash原理、内核链表深度考点3.7.1 bash外壳进程原理3.7.2 内核list_head 多链表超级重点3.7.3 PID上限 进程数量限制3.7.4 内存泄漏终极考点3.7.5 完整误区大全12条终极版4. 进程的“朋友圈”环境变量4.1 环境变量是什么鬼4.2 环境变量的绝技5. 操作系统的大忽悠程序地址空间5.1 你的内存不是你的内存5.2 见证奇迹的时刻同地址不同值5.3 为什么OS要“画大饼”5.3.1 页表权限拦截字符串常量区崩溃案例5.4 结合虚拟内存理解进程挂起5.5 内核核心思想再谈「先描述后组织」5.6 三大核心结构体最终从属关系PCB / mm_struct / list_head6. 全文完整流程总结 吐血整理小白也能秒懂的 Linux 进程概念大揭秘硬核详细版哈喽各位未来的技术大牛们今天咱们就来一场深度探索用最通俗幽默的语言配合丰富的概念图和代码示例带你把进程的底裤彻底看穿1. 祖师爷的宝训冯·诺依曼体系结构在聊进程之前咱们得先拜一拜祖师爷冯·诺依曼。我们现在用的笔记本、不常见的服务器绝大部分都死死守着他老人家定的规矩。1.1 硬件的“权力游戏”计算机就是个大工厂里面有几个核心部门输入设备键盘、鼠标、麦克风等负责收集外界情报。输出设备显示器、打印机等负责向外界展示成果。中央处理器 (CPU)包含运算器和控制器是工厂里唯一干活的“超级打工人”。存储器 (内存)核心的中转仓库。概念图冯·诺依曼数据流向图[输入设备] — (写入) — 【存储器 (内存)】 — (读取) — [输出设备]↑ ↓(读取) (写入)【 CPU 】这里有个铁律所有设备都只能直接和内存打交道CPU这位高冷员工只认内存绝不和外设直接说话外设想输入或输出数据也只能乖乖往内存里写或者从内存里读。示例你用QQ给朋友发消息数据是怎么跑的你敲击键盘输入设备数据先加载到内存。CPU从内存读取数据进行处理比如加密写回内存。最后内存把数据送给网卡输出设备发出去。2. 计算机里的大管家操作系统 (OS)2.1 操作系统是个啥在整个计算机软硬件架构中操作系统的定位非常清晰它就是一款纯正的“搞管理”的软件。对下要管理好各种软硬件资源CPU、内存、硬盘对上要给咱们的应用程序提供一个舒舒服服的执行环境。2.2 管理的六字真言先描述再组织操作系统是怎么管理的咱们想象一下大学里的校长OS他不需要认识每一个学生他只需要辅导员提供学生的数据信息即可。描述在C语言中就是用struct结构体把对象的属性姓名、学号、成绩记录下来。组织用链表或其他高效的数据结构把这些结构体串起来方便增删查改。概念图系统调用与银行柜台操作系统就像一家银行底层的硬件就是金库。银行绝对不允许你直接进金库拿钱而是给你提供了**“综合窗口”系统调用 System Call**。开发者通过调用OS提供的接口如printf底层调用的系统接口来安全地使用硬件资源。扩展比如面向对象的CJava等都有类和容器类即是对物品的描述容器即是组织3. 揭开进程的神秘面纱3.1 到底什么是进程课本上说进程是“运行中的程序”。但在内核眼里进程 内核数据结构 (task_struct) 自己的程序代码和数据。3.2 进程的“户口本”PCB为了管理进程Linux内核给每个进程建了个档案叫做进程控制块PCB具体名叫task_struct。里面记录了标示符 (PID)进程的唯一身份证号。状态是跑着呢还是睡着呢还是死了上下文数据进程被切走时CPU寄存器里保存的临时数据方便下次回来接着跑。所有的task_struct会以双向链表的形式被内核组织起来。这里很多同学容易产生错觉觉得 PCB (task_struct) 就是一个 “类”。但要注意task_struct 是C 语言的 struct 结构体并不是面向对象里的类。C 语言的结构体只能存放成员变量不能存放成员函数它的作用仅仅是把对象的各类描述信息打包收纳在一起。操作 PCB 的相关逻辑函数全部是独立写在结构体外部通过传入结构体指针完成操作函数并不属于结构体本身。3.2.1 cwd 和execwd 与 exe 核心对比表对比维度cwdCurrent Working Directory/proc/[PID]/cwdexe/proc/[PID]/exe核心含义进程的当前工作目录可理解为进程「运行时干活的文件夹」进程对应的磁盘可执行二进制文件本体路径可理解为进程「工具本身存放的位置」内核存储位置内核PCBtask_struct→fs_struct结构体的pwd字段内核PCBtask_struct中记录的可执行文件路径字段核心作用进程使用相对路径访问文件时的基准目录决定相对路径文件的读写位置标识该进程由哪个磁盘上的可执行程序启动用于定位进程的原始程序文件fork子进程继承规则子进程直接继承父进程的cwd子进程直接继承父进程的exeexec加载新程序后的变化保持不变继续沿用当前进程的工作目录同步更新为新加载的可执行文件路径用户态修改方式可通过chdir()系统调用修改仅影响当前进程不影响父进程/其他进程用户态无直接修改的系统调用仅exec加载新程序时会自动更新对应文件/目录被删除后的表现符号链接标记(deleted)进程仍持有目录句柄已打开的文件可正常读写符号链接标记(deleted)进程仍可正常运行内核持有文件描述符磁盘数据不会被回收核心易混点不是环境变量PWDPWD是用户空间字符串cwd是内核维护的真实进程属性二者可不一致不是进程的运行目录exe是程序文件的静态路径和进程当前在哪运行完全无关典型场景示例在/root目录执行/opt/bin/appcwd为/root在/root目录执行/opt/bin/appexe为/opt/bin/app3.3 影分身之术fork()在Linux下怎么创造新进程用fork()系统调用对话知识点补充fork返回值底层原理pid_t id fork();并不是同一个进程拿到两个返回值。fork调用成功会产生两次返回在父进程中返回子进程的PID正数赋值给父进程自己局部变量id在子进程中返回0赋值给子进程自己局部变量id。⚠父子是两个独立进程各自拥有一份独立的id变量不是共享同一个变量。之后父子两个进程拿着属于自己的id各自独立执行if‑else判断自己对号入座进入对应分支。父id0进入else子id0进入else if(id0)分支。两个进程并行向后跑代码所以会看到父子都打印输出。fork()有个极其反人类的特性它有两个返回值执行后父子进程代码共享但数据会各自开辟空间私有一份采用写时拷贝。 深度代码示例#includestdio.h#includeunistd.hintmain(){pid_tidfork();// 召唤影分身if(id0){// 创建失败return1;}elseif(id0){// 这一部分只有【子进程】会执行printf(我是儿子我的PID: %d\n,getpid());}else{// 这一部分只有【父进程】会执行id就是儿子的PIDprintf(我是老爸我的PID: %d\n,getpid());}return0;}拓展实验代码带全局变量、while循环版本观察写时拷贝现象#includestdio.h#includeunistd.h#includesys/types.hintgval100;intmain(){printf(进程开始运行 ,pid: %d\n,getpid());pid_tidfork();if(id0){perror(fork);return1;}elseif(id0){printf(我是一个子进程 !, 我的pid: %d, 我的父进程id: %d\n,getpid(),getppid());sleep(5);while(1){sleep(1);printf(子进程修改变量 : %d-%d,gval,gval10);gval10;printf(我是一个子进程 !, 我的pid: %d, 我的父进程id: %d\n,getpid(),getppid());}}else{while(1){sleep(1);printf(我是一个父进程 !, 我的pid: %d, 我的父进程id: %d, gval: %d\n,getpid(),getppid(),gval);}}return0;}✨现象解读子进程内部不断修改全局变量gval父进程打印出来的gval始终是初始值。很多同学会误以为全局变量父子共享实际fork触发写时拷贝修改时父子拥有各自独立的数据副本互相隔离。⚠注意打印出来的ppid只是拿到父进程的PID数字并不代表那个进程在执行本程序代码。3.4 进程的“七情六欲”状态流转进程有几种典型状态R (Running)运行态。并不意味着正在疯狂占用CPU只要在运行队列里排队随时准备跑的都是R态。S (Sleeping)浅度睡眠。等事件完成比如等键盘输入随时能被叫醒可中断睡眠。D (Disk sleep)深度睡眠。不可中断睡眠。✅底层核心原理老师课堂考点进程正在做磁盘IO、硬件读写手里握着硬件锁、缓冲区资源。如果此时允许被kill、被信号唤醒进程直接退出硬件还在写入、锁不释放、数据没收尾直接导致文件损坏、文件系统崩溃。所以D状态屏蔽所有软件信号kill -9无效只能等待硬件IO完成、硬件中断唤醒进程。系统内存爆满触发OOM时OOM会跳过D进程防止IO半路中断丢数据。T (Stopped)暂停状态。被信号强制暂停了比如发了SIGSTOP信号。Z (Zombie)僵死状态。进程退出了等老爸来收尸。3.5 让人头疼的“僵尸”与“孤儿”‍♂️ 僵尸进程 (Zombie)子进程退出父进程不调用wait回收PCB残留内核中。危害用户层代码、堆栈全部释放内核task_struct不释放 → 内核内存轻微泄漏PID号不回收复用 → PID资源耗尽严重。❗重点不是内存满崩是PID编号被僵尸占死内存空闲也无法创建新进程。 孤儿进程父进程先退出子进程被 systemd(1号进程) 领养自动回收无危害。3.6 谁先吃肉进程调度与 O(1) 算法CPU资源有限进程需要竞争这就有了优先级相关的一整套体系nice、priority、counter。概念区分nice用户层可见的谦让度范围-20 ~ 19只是对外接口不直接参与内核调度运算。nice值越小代表越不谦让希望拿到更多CPU时间。普通用户只能调大nice降低优先级root才允许设置负数nice。priority内核静态优先级O(1)调度范围60‑99。换算公式priority 80 nice真正用来挂调度队列链表。counter时间片由priority计算得出进程拿到的CPU时间配额时钟中断下不断递减counter 0进程移入过期队列。❓为什么要有nice值nice是给用户提供一套带权限限制的稳定接口。内核内部priority不希望直接暴露给用户。普通用户只能“谦让”不能恶意抢占CPUroot才可以提升优先级。同时隔离内核内部实现调度器版本变更用户层脚本不需要修改。❓为什么用户不能直接操作task_struct里的priority用户态/内核态硬件隔离task_struct存放在内核地址空间用户进程不能直接读写内核内存只能通过系统调用陷入内核代为修改。权限管控如果直接暴露priority普通用户可以随意把自己改成最高优先级恶意抢占CPU造成整机卡死nice封装权限校验逻辑在系统调用内部。内外解耦priority是调度器内部实现不同调度器版本数值规则会变nice作为稳定用户API内核升级用户脚本不需要改动。修改优先级不是单纯赋值改priority需要把进程从旧链表摘下、重新计算队列下标、挂入新链表、更新bitmap位图一整套队列维护逻辑必须由内核完成用户直接改字段会造成runqueue数据错乱崩溃。⚠重要考点修改nice不会改变当前已经拿到手的counter剩余时间片只会影响下一轮轮转分配的counter大小。为了让调度快如闪电Linux 2.6 内核搞了个牛逼的O(1) 调度算法概念图O(1) 调度队列机制内核维护了两个队列活动队列 (active)和过期队列 (expired)。队列里有140个格子数组queue[140]对应不同的优先级。系统还搞了个 5*32 位的bitmap位图。CPU找进程时不遍历链表直接查位图瞬间就能找到哪个优先级格子里有进程时间复杂度永远是常数。活动队列的进程跑完了就把指针跟过期队列一换交换active和expired指针继续跑永不停歇3.7 高频误区汇总、bash原理、内核链表深度考点3.7.1 bash外壳进程原理bash本身也是一个普通进程。打开终端系统就会启动一份bash进程。我们在命令行敲下指令ls、pwd、./a.outbash会调用fork创建子进程再调用exec替换子进程的程序镜像去执行对应的命令/可执行文件。bash是父进程我们执行的命令几乎全部是bash的子进程。cd、export这类内置命令不会fork子进程直接在bash进程内部执行。如果cd创建子进程去执行子进程修改cwd父bash的工作目录完全不受影响cd就会失效。简单流程终端启动 → 创建bash进程 用户输入ls → bash fork()出子进程 → 子进程exec(ls) → ls运行结束退出 → bash继续等待输入3.7.2 内核list_head 多链表超级重点Linux内核采用侵入式双向链表 list_headstructlist_head{structlist_head*next,*prev;};structtask_struct{structlist_headtasks;// 全局进程链表structlist_headrun_list;// 调度队列链表structlist_headchildren;// 子进程链表structlist_headsibling;// 兄弟进程链表};✅核心作用一个进程结构体可以同时挂在多条不同内核链表上各司其职、互不干扰。tasks让系统能遍历所有进程run_list让调度器挑选就绪进程children/sibling维护父子、兄弟进程关系如果只有一组指针进程同一时刻只能在一条链表操作系统完全无法管理。✅container_of原理链表指针只指向list_head成员不指向整个task_struct。需要通过结构体偏移量回推整个PCB地址。写错member参数直接内存越界、内核崩溃。3.7.3 PID上限 进程数量限制pid_max 只是编号上限64位系统最大 41943042^22写更大无效PID正常退出可循环复用不会耗尽真正卡死的是僵尸进程僵尸不释放PID占住编号不归还PID耗尽内存再大也无法fork新进程真正限制系统最大进程数的是threads-max系统全局最大任务数ulimit -u单用户最大进程数内核内存大小3.7.4 内存泄漏终极考点短生命周期进程就算代码泄漏进程退出OS全部回收无害。常驻内存进程nginx、mysql、360、服务进程永久不退出泄漏内存不断累积越跑越吃内存、最终OOM被杀危害极大。360难删原理操作系统原理多进程守护 系统服务自启 内核驱动拦截删除常驻系统普通用户无法终结。3.7.5 完整误区大全12条终极版❌fork同一个进程返回两个值✅父子两个进程分别返回各自局部变量独立❌fork后全局变量共享✅只读共享写时拷贝修改互相隔离❌task_struct链表是父子关系✅全局链表父子靠parent指针❌虚拟地址相同物理地址相同✅虚拟地址进程私有可重复映射不同物理内存补充写时拷贝COW关键易错点gval拿到的是虚拟地址发生写时拷贝之后虚拟地址数字完全不变变化的只有页表映射关系背后映射到了一块全新的物理内存。C语言用户层拿不到物理地址所以打印地址看不出变化。虚拟地址相当于门牌号物理内存相当于真实房子房子换了门牌号保持原样。❌子进程全盘复制父进程✅PID/PPID全新上下文快照继承❌僵尸进程占用代码数据内存✅用户资源全释放只剩内核PCB❌cd命令创建子进程✅内置命令bash内部执行❌D状态进程可以被kill -9杀死✅D状态屏蔽所有软件信号OOM也杀不动❌所有IO都会进入D状态✅普通网络IO是S状态硬件块设备IO才是D❌list_head指针直接指向task_struct✅只指向内部成员必须container_of回推❌一个进程只能挂一条内核链表✅可嵌入多个list_head同时挂多条链表❌pid_max决定系统最大进程数✅pid_max是编号池真正限制是threads-max、内存、ulimit4. 进程的“朋友圈”环境变量4.1 环境变量是什么鬼平时敲ls指令直接就能出结果但跑咱们自己的程序得敲./a.out带上路径。为啥因为系统里有个叫PATH的环境变量它指定了命令的默认搜索路径。系统在PATH的路径里找到了ls但找不到你的a.out。补充shell本地变量 vs 环境变量本地变量var123仅当前bash可用不会被子进程继承子进程getenv()拿到NULL。环境变量export var123存入bash环境表fork出来的所有子进程都可以通过getenv()读取。子进程内部调用setenv()仅仅修改自己进程的环境变量副本完全无法改变父bash的变量。getenv()返回的内存只能读禁止直接改写。4.2 环境变量的绝技每个程序都会收到一张环境表字符指针数组environ。最牛的是环境变量具有全局属性可以被子进程继承下去示例在代码里怎么拿到环境变量用系统调用getenv(PATH)就行5. 操作系统的大忽悠程序地址空间5.1 你的内存不是你的内存C语言老师告诉我们内存分栈区、堆区、未初始化数据、代码段。但实际上我们用C/C打印出来的地址全都是虚拟地址真正的物理地址用户一概看不到由OS统一管理。虚拟地址空间分区概览低地址 → 高地址正文代码段存放只读程序指令初始化数据段data初始化全局、static变量BSS段未初始化全局/static变量内核自动清零堆heapmalloc申请地址向上增长大片镂空空洞无页表映射访问直接段错误mmap共享区动态库、文件映射栈stack局部变量地址向下增长栈顶端存放argv命令行参数、env环境变量最高段内核空间用户态无权访问5.2 见证奇迹的时刻同地址不同值看下面这段神奇的验证代码 深度代码示例#includestdio.h#includeunistd.hintg_val0;// 全局变量intmain(){pid_tidfork();if(id0){// 子进程先跑修改变量g_val100;printf(子进程: 值 %d, 地址 %p\n,g_val,g_val);}else{// 父进程等一会再跑sleep(3);printf(父进程: 值 %d, 地址 %p\n,g_val,g_val);}return0;}输出结果子进程: 值 100, 地址 0x80497e8 父进程: 值 0, 地址 0x80497e8惊不惊喜父子进程打印的地址一模一样但里面的值却不一样这说明这个地址绝对不是物理地址。5.3 为什么OS要“画大饼”OS给每个进程都画了一张叫做mm_struct的大饼虚拟地址空间。概念图虚拟内存到物理内存的映射【进程A的虚拟地址空间】 (地址: 0x80497e8)|【页表 (映射表)】 ---------------- 【物理内存】(某真实地址存了100)【进程B的虚拟地址空间】 (地址: 0x80497e8)|【页表 (映射表)】 ---------------- 【物理内存】(另一真实地址存了0)为什么不直接操作物理内存非要搞这么复杂安全风险控制如果进程能直接访问物理内存流氓软件就能随意修改其他程序的内存甚至系统内核导致死机。有了虚拟地址所有访问都必须经过OS的页表审查。解耦合与效率延迟分配进程申请内存时如mallocOS只在虚拟地址空间里给你分地盘。等你真正去写数据时OS才会在物理内存里给你找个位置并建立映射。这就做到了进程管理和内存管理的完美解耦。统一视角让每个进程都觉得自己拥有连续、完整的内存空间程序在物理内存中其实可以见缝插针地随便放大大提高了空间利用率。5.3.1 页表权限拦截字符串常量区崩溃案例char*strhelloworld;*strH;字符串字面量存放在只读字符常量区对应页表项标记只读权限。当代码尝试写入该地址MMU硬件在地址翻译时检测权限冲突触发异常内核发送SIGSEGV段错误程序直接崩溃。重点不是C语言语法禁止修改是页表硬件权限拦截保护内存。对比char str[] helloworld; str[0]H;可以正常修改字符串拷贝到栈空间栈内存页具备读写权限。5.4 结合虚拟内存理解进程挂起很多同学容易混淆进程睡眠和进程挂起借助虚拟内存、页表的知识就很好理解。普通睡眠(S状态)进程PCB驻留内存进程依旧占用自己的物理内存仅仅不参与CPU调度等待事件唤醒。物理内存数据不会被搬走。进程挂起换出到Swap当系统物理内存资源紧张操作系统会把进程挂起task_struct、mm_struct、虚拟区间描述全部保留在内核虚拟地址空间完整保留图纸不丢。修改该进程的页表项不再指向物理内存标记数据存放于磁盘Swap分区。进程原本占用的物理内存页全部回收分配给其他急需内存的进程。进程自身完全感知不到它眼中的虚拟地址空间没有任何变化。当进程需要恢复运行CPU访问虚拟地址页表提示页面在磁盘触发缺页异常。内核将Swap磁盘的数据重新加载回物理内存更新页表映射进程就可以继续执行。核心没有虚拟内存与页表机制就实现不了进程挂起。如果没有虚拟地址隔离一旦把进程数据丢到磁盘程序地址全部错乱无法复原。区分重点睡眠进程数据还在物理内存只是不跑CPU。挂起进程数据挪到磁盘Swap物理内存释放。5.5 内核核心思想再谈「先描述后组织」mm_struct这个内核结构体仅仅是对虚拟地址空间的描述、记账相当于一张图纸。它记录各个段的起止地址、虚拟区域链表、页表基地址但是它本身不做地址翻译不操作硬件。真正完成内存组织依靠两部分CPU硬件MMU内存管理单元页表每一次访问内存由硬件完成虚拟地址到物理地址的翻译同时做权限校验。操作系统内核软件逻辑内核负责修改mm_struct登记虚拟地址区间分配回收物理内存填充修改页表处理缺页异常、swap挂起换入换出。逻辑链条mm_struct保存页表基地址 → 内核操作页表 → CPU‑MMU硬件完成地址翻译。举个例子调用malloc仅仅修改mm_struct、vm_area_struct完成虚拟地址预约只有访问内存触发缺页异常内核才分配物理内存硬件完成映射。图纸只规划地盘真正盖房子干活的是系统和硬件。拓展历史小彩蛋虚拟内存并不是某个人灵光一闪的天才脑洞。上世纪50‑60年代物理内存硬件价格昂贵内存资源紧缺倒逼出这套技术。1956年德国博士生提出虚拟内存理论构想1962年英国曼彻斯特大学Atlas计算机世界第一台实现分页虚拟内存的机器后续Unix、Mach微内核迭代出写时拷贝COWLinux使用mm_struct、vm_area_struct把这套模型落地实现。⚠虚拟地址翻译依赖CPU硬件MMU没有MMU光靠内核结构体虚拟内存完全无法工作。5.6 三大核心结构体最终从属关系PCB / mm_struct / list_head这是整篇博客最核心的底层架构串联所有知识点彻底理清三者从属、关联关系list_head 与 PCBtask_structlist_head是PCB内部嵌入的成员依靠侵入式链表设计一个task_struct里面可以塞多个list_head。让同一个进程PCB可以同时挂载到多条不同内核链表全局进程链表、调度运行队列、父子兄弟链表实现多维度管理配合container_of宏从链表成员反向拿到完整PCB结构体。mm_struct 与 PCBtask_structmm_struct是task_struct内部的指针成员每一个用户进程都会关联一份mm_struct用来完整描述该进程的整套虚拟地址空间mm_struct维护各个虚拟段、页表基地址和硬件MMU配合完成地址映射。整体关系总结task_struct(PCB)是进程的总档案里面既包含调度、状态、PID、上下文、文件信息又通过list_head挂入各类内核链表做组织管理再通过mm_struct指针管理整个进程虚拟内存。完美践行操作系统“先描述后组织”的核心思想。6. 全文完整流程总结从计算机硬件到进程运行整套链路可以完整串起来冯·诺依曼体系规定硬件的数据交互规则操作系统作为软硬件中间层遵循先描述后组织的思想使用C语言结构体描述一切软硬件资源链表完成组织管理。当我们在bash终端敲下一条命令bash进程调用fork()创建子进程依靠写时拷贝复制父进程PCB、mm_struct、环境变量、cwd等信息子进程再调用exec替换程序镜像生成我们需要执行的业务进程。每一个新进程都会生成自己独立的task_structPCB内部嵌入多份list_head挂载到内核不同链表PCB内部指针指向专属的mm_struct虚拟地址空间由mm_struct配合页表、CPU的MMU硬件完成虚拟地址到物理内存的映射。内核调度器O(1)算法根据nice、priority、时间片从调度链表挑选进程上CPU运行进程运行过程中会发生状态切换运行R、浅睡眠S、深度不可中断D、暂停T进程退出后如果父进程没有wait回收就变成僵尸进程PCB残留在内核占用PID资源父进程提前退出则子进程被1号进程领养。进程访问内存拿到的全部是虚拟地址malloc仅仅预约虚拟地址缺页异常才分配真实物理内存内存紧张时操作系统可以把进程换出到Swap磁盘实现进程挂起依靠虚拟内存机制保证程序无感知。一句话概括进程 task_struct(PCB描述信息 list_head链表组织) mm_struct虚拟地址空间 代码数据。硬件MMU完成地址翻译调度器完成CPU时间分配系统调用完成用户态与内核态交互。