Linux 进程概念 (三) (进程状态,僵尸进程,孤儿进程,进程优先级)
目录一、操作系统学科上的进程状态运行状态阻塞状态运行队列和阻塞队列挂起状态运行挂起和阻塞挂起二、linux中对应的进程状态R运行状态S睡眠状态D磁盘休眠状态T停止状态killX死亡状态前台进程和后台进程三、僵尸进程Z僵尸状态什么是僵尸进程?僵尸进程的危害问题四、孤儿进程什么是孤儿进程?查看1号进程孤儿进程为什么会被领养?为什么会被1号进程领养?六、进程优先级什么是进程优先级?为什么要有进程优先级?查看系统进程PRI和NI修改进程优先级改法 : 使用topPRI(new)PRI(old)NI问题:实时操作系统与分时操作系统七、进程之间的关系八、总结一、操作系统学科上的进程状态上述为操作系统学科上的进程状态这里我们仅针对进程状态特定的介绍一下运行阻塞挂起三种状态运行状态每个 CPU 都维护一个运行队列该队列通过结构体实现包含指向进程控制块PCB队列头部和尾部的两个指针。运行队列本质上是大量进程的排队机制实际排队的是进程的 PCB。PCB 通过指针关联其对应的进程代码和数据。虽然称为运行队列但并非所有进程都在执行运行指的是这些进程已准备就绪可随时被调度器选中执行。调度器会优先选择队列首部的 PCB 进行调度。当头部进程在 CPU 上执行一段时间后会被重新放回队列尾部继续排队。这种频繁将进程调入调出 CPU 的操作称为进程切换。CPU 的运算速度极快达到纳秒级别远超人类感知。以日常电脑为例同时运行多个应用程序如音乐播放器、浏览器、绘图软件等看似这些进程在并行执行实则不然。由于 CPU 的超高速度它实际上是在极短时间内依次调度各个进程执行从队列头部取出进程运行片刻后放回尾部如此循环往复。同一时刻CPU 只能执行一个进程。每个 PCB 都记录有时间片信息用于限制进程占用 CPU 的时间如 10ms。这确保了不会有进程独占 CPU 直至执行完毕。在极短时间周期内CPU 能完成对整个运行队列的多次轮询使得所有进程代码都能得到执行这种现象称为并发执行。处于运行队列中的所有进程都处于运行状态简称为 Rrun状态。阻塞状态阻塞的一种现象 : 比如scanf,在等待输入时,等待键盘上面的待输入数据,其实就处于阻塞状态我们知道计算机系统由各类外部设备外设组成操作系统作为管理软硬件资源的软件对外设的管理也遵循先描述后组织的原则。具体实现方式是为每个设备创建一个硬件设备控制块struct dev然后通过数据结构如单链表将这些控制块组织起来便于统一管理。在这些硬件设备控制块中包含指向进程控制块PCB的指针。当进程需要访问外设如键盘时若设备尚未就绪例如用户未输入数据进程将进入等待状态。此时该进程的PCB会被链接到设备控制块DCB的PCB指针上形成一个专门的队列在操作系统中称为阻塞队列。若后续有其他进程也需要访问同一设备它们将被依次添加到阻塞队列的末尾继续等待。处于阻塞队列中的所有进程其状态均被标记为阻塞状态。这种机制确保了设备资源的合理分配和进程的有序等待。运行队列和阻塞队列内核里的“运行队列”和“阻塞队列”本质是两种存放PCB进程控制块的“容器” ——所有进程的状态变化底层都是PCB在这两个队列之间的移动没有例外。运行队列Run Queue内核维护的“待CPU执行”PCB集合里面的每个PCB都满足“资源全就绪只差CPU”。队列按进程优先级排序Linux里每个CPU核心都有自己的运行队列避免竞争。阻塞队列Block Queue不是单一个队列而是按“等待的资源类型”分的多个队列如磁盘IO阻塞队列、网络阻塞队列等外设设备的队列。里面的PCB都在“等资源”比如等磁盘读完数据、等网卡收到网络包。判断处于运行状态和阻塞状态的核心判断依据就是进程在内核的哪个队列中直接对应运行/阻塞状态。运行状态进程在就绪队列中要么正在用CPU要么排队等CPU随时能执行。阻塞状态进程不在就绪队列而是在阻塞队列如等待磁盘IO的队列、等待网络的队列等外设相关的队列中必须等资源就绪(如scanf输入语句)才能回到运行队列。挂起状态当操作系统内存资源严重不足时为确保系统安全、稳定和高效运行必须采取措施释放内存。由于计算机外设状态可能未就绪导致大量进程处于阻塞等待状态操作系统会优先处理这些阻塞进程。从技术角度看加载到内存中的PCB进程控制块和程序对应的代码数据组成称为进程。当大量进程同时存在时会占用可观的内存空间。处于阻塞状态的进程只需保留PCB即可因其当前无法执行操作代码数据可以暂时移出内存。考虑到磁盘空间远大于内存操作系统会将阻塞进程的代码数据移至磁盘交换区(swap分区)即swap out这一过程称为换出或进程挂起这个状态叫做挂起状态。当外设就绪后进程结束等待状态操作系统会将其代码数据从磁盘换入内存即swap in并将进程加入CPU运行队列使其恢复运行状态。需要注意的是这种内存资源严重不足的情况较为罕见进程挂起并非常态。只有当内存极度紧张时才会触发此外还可能因调试需求、系统管理或进程优先级策略等其他因素引发挂起操作。运行挂起和阻塞挂起运行挂起 : 进程原本在运行/就绪队列等CPU因系统资源紧张如内存不足内核将其从运行队列移出状态标记为“运行挂起”暂时不参与CPU调度。阻塞挂起 :进程原本已在阻塞队列等磁盘/网络资源且内存极度紧张内核会把它的PCB和数据从物理内存换出到磁盘swap分区状态变为“阻塞挂起”连阻塞队列都暂时退出。核心是挂起的原因不同运行挂起是“进程主动放弃CPU暂离运行队列”阻塞挂起是“进程等资源时连阻塞队列都暂离被换出内存”二者都是进程暂时无法执行但触发场景和内核处理逻辑完全不同。二、linux中对应的进程状态上面只是操作系统学科上的对进程状态的理解接下来我们着重看一下在Linux中对进程状态的理解掌握Linux中的进程状态可以更好深入我们对进程状态的理解!下面是Linux内核中定义进程状态的代码片段它列出了进程的核心状态://linux源代码如下 static const char * const task_state_array[] { R (running) //运行 0 S (sleeping) //睡眠 1 D (disk sleep) //深度睡眠 2 T (stopped) //停止 4 t (tracing stop)//追踪停止 8 X (dead) //死亡 16 Z (zombie), //僵尸 32 };Rrunning运行状态 —— 进程处于就绪或运行队列中可能正在执行或等待CPU调度属于活跃的可执行状态。Ssleeping睡眠状态 —— 进程因等待I/O操作或信号等资源而进入阻塞队列可被信号唤醒属于可中断的休眠状态。Ddisk sleep深度睡眠状态 —— 进程正在等待磁盘I/O等关键资源无法被信号中断强制终止可能导致数据损坏仅在资源就绪时自动恢复。Tstopped停止状态 —— 进程被主动暂停如使用CtrlZ处于运行挂起状态可通过fg/bg命令恢复执行。ttracing stop跟踪停止状态 —— 进程因调试器如gdb跟踪而暂停仅出现在调试场景中。Xdead死亡状态 —— 进程已完成终止并释放资源属于瞬时状态通常无法通过监控工具观察到。Zzombie僵尸状态 —— 进程已终止但父进程尚未读取其退出状态仅保留进程控制块PCB需父进程处理后才能完全释放资源。R运行状态R运行状态running并不意味着进程一定在运行中它表明进程要么在CPU运行中要么在CUP对应的运行队列里(运行就绪)为什么我们的进程处于运行状态代码中是 while(1) 的无限循环没有 sleep 、等待输入等阻塞操作进程会持续占用CPU执行循环逻辑——因此显示其状态为 R 前台运行/就绪符合“无阻塞、持续执行”的进程特征。,S睡眠状态S睡眠状态sleeping意味着进程在等待事件完成同时也叫做可中断睡眠浅度睡眠即睡眠期间可以被唤醒对应操作系统学科中的阻塞状态为什么我们的进程会处于睡眠状态代码里的 scanf(%d, a) 会让进程进入“等待终端输入”的阻塞状态——此时进程不需要占用CPU会被系统调度到阻塞队列因此 ps 显示其状态为 S 前台可中断睡眠。S状态可使用kill被中断 , 也能说明其是浅度休眠D磁盘休眠状态D磁盘休眠状态disk sleepD状态是Linux特有的一个进程状态, 但它是不可被中断的休眠状体 , 也叫做深度休眠状态处于这个状态的进程通常会等待IO的结束不响应任何请求处于深度睡眠的进程不响应操作系统的任何请求也无法被 kill -9杀死让我们以进程向磁盘写入数据为例进行说明以下场景为假设情况并非真实案例。当进程尝试向磁盘写入数据时假设磁盘空间不足磁盘接收数据并开始写入操作此时进程会等待磁盘返回写入结果。恰巧此时操作系统面临内存资源严重不足的情况急需释放内存空间。操作系统发现该进程处于等待状态便会认为系统内存即将耗尽运行效率严重下降而这个进程却还在悠闲等待。于是操作系统直接终止了该进程。当磁盘尝试写入数据时发现剩余空间确实不足便会判断这个数据量太大无法写入但剩余空间足够处理其他较小的数据。于是磁盘直接丢弃该进程的数据转而处理其他进程的较小写入请求。完成操作后磁盘准备向原进程返回响应却发现该进程已不存在。此时进程已被操作系统终止其对应的代码和数据随之释放同时磁盘也因空间不足丢弃了数据导致数据永久丢失。从系统角度来看操作系统为维护内存资源而终止进程这是合理的进程在等待磁盘响应时被终止属于受害者磁盘因空间不足选择优先处理较小数据也是合理行为。这种情况下很难界定责任归属但频繁的数据丢失对用户而言是不可接受的。为解决该问题系统会将此类进程设置为深度睡眠状态进程不会响应操作系统的任何请求因此不会被强制终止进程的代码和数据得以保留只有收到磁盘的明确响应后进程才会恢复运行。通过这种机制上述数据丢失问题就能得到有效避免。T停止状态停止状态stopped/tracing stop进程可通过信号暂停或恢复运行在学习阶段通常不区分stopped和tracing stop状态追踪停止状态tracing stop特指调试过程中的进程暂停状态例如使用gdb调试时进程会在断点处暂停此时调试器可查看变量信息并控制进程执行T暂停状态的主要作用实现对进程的调试控制便于检查程序运行时的状态信息当程序运行到第8行的断点处之后使用ps查看进程状态进程对应进程状态为t即当前进程处于追踪停止状态killkill指令可以发送信号进行控制进程kill -19 进程标识符 将进程暂停kill -18 进程标识符 让进程继续运行kill -9 进程标识符 杀死进程终止进程X死亡状态X状态彻底死亡是Z状态僵尸是的前一步二者是“先僵尸、再彻底死亡消失”的先后关系进程先终止→进入Z状态留着PCB等父进程回收是“死了但没清干净”父进程回收PCB后→Z状态转为X状态瞬时过渡“彻底清干净了”X状态结束→进程从系统中完全消失生命周期结束。简单说Z是“死了没埋”X是“埋了的最后一瞬间”先Z后XX之后啥都没了。关于Z僵尸状态和僵尸进程 , 我们在后面再展开讲解.前台进程和后台进程什么是前台进程和后台进程?进程状态后面带 的就是前台进程 , 不带 就是后台进程怎么删除?删除前台进程 : 前台进程正在占用终端直接按 CtrlC 即可终止删除后台进程 : 先通过 ps axj | grep myprocess 找到后台进程的PID再执行 kill PID 如 kill 1234 若进程无响应用 kill -9 PID 强制终止。应用场景:1. 前台进程用在需要实时交互的场景比如运行需要你手动输入、实时看结果的程序——像执行 ./myprocess 后要通过键盘输参数 scanf 、实时看程序打印的日志 printf 或用 vim myprocess.c 编辑文件必须前台得手动敲键盘操作。核心是进程要和你实时互动离不开你的操作。2. 后台进程用在无需手动干预、要长期跑的场景比如运行耗时的任务——像编译大项目 make 、后台统计数据 ./count_data 、启动服务程序如后台挂着的小脚本。核心是进程自己能跑完不用你盯着你还能在终端干别的事不耽误用电脑。所以区分前后台进程的核心就是: 是否能从键盘中获取数据输入;能直接接收键盘输入的就是前台进程不能接收键盘输入的就是后台进程并且同一终端里同一时间只能有1个前台进程——它会独占终端的输入响应比如CtrlC、键盘输入你得等它结束或切到后台才能再运行新的前台进程而后台进程可以同时有多个——它们不抢终端输入能在后台并行运行你还能正常在终端输新命令。比如你可以同时让3个程序在后台跑 但是同一时间只有1个能在前台“霸占”你的键盘操作。三、僵尸进程Z僵尸状态什么是僵尸进程?僵尸进程zombies子进程终止的时候如果父进程没有主动回收子进程的信息那么子进程会让自己一直处于Z僵尸状态即对应子进程相关资源尤其是task_struct结构体不能释放首先我们在代码中先让父进程 fork() 创建子进程 , 然后使父子进程都进入 while(1) 死循环每隔1秒打印一次信息。当开始运行时 , 父子进程都执行 sleep(1) 时都会进入S状态可中断睡眠前台进程组这是因为 sleep 本质是“等待时间资源”符合“S状态是等待资源”的逻辑。当我们用 kill -9 27250 杀死子进程后子进程变成了Z状态僵尸前台进程组并且标注了 defunct 无效——这是因为父进程还在死循环里没调用 wait() 去回收子进程的PCB所以子进程进入了僵尸状态。核心结论:进程执行 sleep 会进入S状态子进程被杀死后若父进程不回收会变成Z僵尸状态。僵尸进程的危害子进程的退出状态必须被维持下去因为它要告诉关心它的父进程父进程交给自己的任务自己完成的怎么样了可是如果父进程一直不进行读取那么此时子进程就要一直维持这个Z僵尸状态那么这个退出状态维护的其实是子进程的进程信息即进程的PCB即task_struct结构体那么子进程对应的未进行释放的task_struct结构体会一直会得不到释放就会一直占用空间那么就会造成内存泄露问题关于进程退出与僵尸进程的4个问题:1. 进程退出了 , 退出信息是什么?进程退出时通常会记录以下关键信息退出状态码0表示成功退出非零值表示异常退出终止信号若进程被强制终止如通过kill -9命令会记录相应的信号编号资源使用情况部分系统会记录CPU时间、内存占用等资源使用统计信息2. 进程退出了 , 退出的信息保存在哪里?这些信息存储在task_struct即进程控制块PCB中。PCB是内核为每个进程维护的数据结构即使进程终止其PCB仍会暂时保留退出状态直到被父进程读取后才会释放。3. 检测Z状态进程 , 回收Z状态进程 , 本质是在做什么?检测机制通过遍历系统进程表筛选出已终止但进程控制块PCB未被释放的进程记录回收机制当父进程获取子进程终止信息后系统将清除该进程的PCB记录使其从进程表中永久移除4. 具体怎么回收?谁来回收?回收主体默认由父进程负责回收子进程若父进程先终止子进程将由 init 进程PID1接管并完成回收。回收方式主动回收父进程调用 wait()或 waitpid()函数获取子进程退出状态并释放其 PCB 资源被动回收若父进程未处理终止父进程后子进程由 init 进程自动回收工具辅助部分系统工具如 wait 类命令可协助触发回收流程。5. 如果进程处于僵尸状态 , 如果我一直不回收 , 会造成什么结果?若父进程不回收子进程 → 子进程会长期处于Z僵尸状态 →其对应的 task_struct 即PCB不会被释放→ task_struct 占用内核内存空间 → 大量堆积时会引发“内存泄漏”的资源占用问题。四、孤儿进程什么是孤儿进程?孤儿进程父子进程中父进程先退出子进程的父进程会被改为1号进程操作系统我们称该子进程被操作系统领养了那么像这种父进程是1号进程的子进程我们称为孤儿进程此时这个子进程就变成了后台进程普通的ctrlc无法进行退出只能使用kill -9 进程标识符将子进程杀死孤儿进程不是进程状态它只是对“父进程已退出、自己还存活的进程”的一种“描述”运行:代码中父进程执行5次循环后就 return 0 退出了但子进程是 while(1) 死循环一直运行——这时候子进程还活在但它的父进程已经没了所以子进程就变成了孤儿进程。还需要注意的是 :1.当进程变为孤儿进程时, 这个进程会自动变成后台进程即父进程先死了 - 子进程变成孤儿 - 自动被initPID1收养 - 同时脱离终端 → 变成后台进程2. 此时这个子进程就变成了后台进程普通的ctrlc无法进行退出只能使用kill -9 进程标识符将子进程杀死因为后台进程不能读终端输入后台进程可以往屏幕上打印但不能等你键盘输入- 后台进程 printf 可以输出往显示器写- 后台进程 scanf、getchar 会被阻塞/直接失败从终端读不行查看1号进程这里不能直接说1号进程是操作系统它是操作系统用户态的核心进程 ,负责初始化系统服务、管理孤儿进程、回收僵尸进程等。简单总结父进程先死→子进程变孤儿→内核让1号进程接管避免无人管理1号进程是系统用户态的核心管理进程。孤儿进程为什么会被领养?当一个进程的父进程先退出这个进程就会变成孤儿进程。为了避免孤儿进程无人管理比如退出后变成僵尸进程Linux内核会自动把它“托付”给一个“兜底”进程——也就是1号进程。为什么会被1号进程领养?1号进程是操作系统启动后创建的第一个用户态进程它的核心职责之一就是管理孤儿进程相当于系统的“进程管家”。所以所有孤儿进程都会被1号进程接管领养后续由1号进程负责回收它的资源。为什么我们按 ctrlc 怎么退不出呢?并且为什么这个孤儿进程从前台进程变为了后台进程?因为父进程先于子进程退出导致子进程成为孤儿进程此时内核会将孤儿进程从原前台进程组移出自动转为后台进程。由于后台进程本身不能从键盘获取输入无法响应 Ctrl C 这类依赖终端的快捷键因此无法通过快捷键终止只能通过 kill 进程PID 命令手动杀死该孤儿进程。所以区分前后台进程的核心就是 :是否能从键盘中获取数据输入;能直接接收键盘输入的就是前台进程不能接收键盘输入的就是后台进程五 : 再次理解进程在学了上面关于进程的状态之后 , 我有一个疑问 :就是我们在 shell 里敲的每一条命令本质都是在启动一个进程吗?是的! 当我们打开的那个黑窗口终端就是 shell 进程时。它的工作只有一个当我们输命令后 , 它就会帮我们创建子进程去执行。举个例子当我们输入在shellz中输入ls指令时 , shell父进程看到你输了 ls ,它调用 fork() 创建一个子进程 , 让这个子进程去执行 ls 程序 , ls 执行完 → 子进程退出 → 变僵尸 , shell 收尸 → 回到你能输命令的状态shell 是父进程你输的每一条命令都是它 fork 出来的子进程六、进程优先级(理论很重要)什么是进程优先级?优先级对于资源的访问谁先访问谁后访问进程的优先级 :CPU资源分配的先后顺序优先级高的进程有优先执行访问权限的权利!也可以理解为 :进程在已经得到某种资源的前提下得到某种资源的先后顺序!为什么要有进程优先级?因为CPU的资源是有限的进程有多个所以就进程之间就必然有竞争关系操作系统必须保证大家良性竞争所以就要确定优先级如果非良性竞争那么就会导致某些进程长时间得不到CPU资源该进程的代码长时间得不到推进——进程的饥饿问题通常来讲非必要情况用户不需要调整优先级进程的优先级的调整由调度器管控调度队列的本质就是确定优先级查看系统进程我们在前已经使用过ps axj查看了系统中进程信息的核心命令——通过ps可以获取进程的ID、状态、优先级、所属用户等关键信息是管理和调试进程的常用工具。ps aux / ps axj / ps ajxa显示一个终端所有的进程包括其他用户的进程。x显示没有控制终端的进程例如后台运行的守护进程。j显示进程归属的进程组ID、会话ID、父进程ID以及与作业控制相关的信息u以用户为中心的格式显示进程信息提供进程的详细信息如用户、CPU和内存使用情况等下面我们再来介绍另一个指令:ps -laF 进程标志代表进程的权限/状态标识如 4 表示进程拥有root权限S 进程状态常见值包括 S 睡眠、 R 运行、 Z 僵尸等UID进程所属用户的ID 0 对应root用户 1001 是普通用户IDPID进程的唯一标识IDPPID当前进程的父进程IDC 进程的CPU使用率百分比PRI进程实际优先级数值越小优先级越高默认值通常为80NI进程的nice值优先级调整参数范围-20~19默认值为0NI越小PRI越低优先级越高ADDR 进程在内存中的地址显示 - 表示进程处于运行状态SZ 进程占用的内存大小单位为页WCHAN 进程等待的内核函数显示 - 表示进程无等待、正在运行TTY 进程关联的终端设备如 pts/0 对应当前SSH终端最后一列时间进程累计占用的CPU时间PRI和NI其实进程优先级可以看成task-struct里面的两个整型变量PRI和NI (proirity和nice)PRI也还是比较好理解的即进程的优先级或者通俗点说就是程序被CPU执行的先后顺序此值越小进程的优先级别越高那NI呢? 就是我们所要说的nice值了其表示进程可被执行的优先级的修正数值PRI值越小越快被执行那么加入nice值后将会使得 PRI 变为PRI(new)PRI(old)NI这样当nice值为负值的时候那么该程序将会优先级值变小即其优先级会变高则其越快被执行所以调整进程优先级在Linux下就是调整进程nice值修改进程优先级改法 : 使用top将程序运行起来复制SSH渠道在复制的渠道中输入top按下回车之后继续输入r按下回车那么此时它会提示要你输入要调整进程对应的PID我们这时就输入进程的PID按下回车然后会显示需要设置的NI值我们就输入需要调整的NI值按R键输入进程PID输入需要调整的NI值此时我们给NI值输入的是10根据PRI(new)PRI(old)NI这个式子新的PRI值就变成了90PRI值由80变成了90就代表着进程优先级被修改了这里改为90后进程的优先级是被降低了PRI(new)PRI(old)NI在Linux系统中每次修改进程优先级时 PRI(old) 默认都是80这是系统的设计规则Linux为了简化优先级计算、统一优先级调整逻辑将 PRI(old) 固定为默认值80——无论进程当前实际优先级是多少再次调整时都会以80作为基准结合新的 nice 值计算新优先级即 PRI(new) 80 NI 。例如若进程当前PRI是90之前设置了 nice10 再次调整 nice-5 时新PRI是 80 (-5) 75 而非 90 (-5) 85 。问题:NI值的范围是多少?在Linux系统的进程调度里 NI Nice值的范围是 -20 到 19共40个梯度。NI值为什么是这个范围?如果NI值范围过大比如允许更低的负值高优先级进程可能长期占用CPU导致低优先级进程“饿死”若正值过大低优先级进程可能永远无法获得资源。-20到19的区间能平衡“高优先级进程优先”和“低优先级进程不被忽略”的需求。进程优先级的变化范围是多少?PRI 进程优先级的范围取决于系统的计算规则常见有两种情况1. 基于 PRI 20 NI 的场景范围是0~39对应NI的-20~19。2. 基于 PRI 80 NI 的场景范围是60~99或100同样对应NI的-20~19。可以高频更改优先级吗?不建议高频更改进程优先级的变化范围也就是NI值范围。高频修改优先级范围会让系统调度逻辑频繁“混乱”可能导致进程调度失控比如高优先级进程持续抢占资源低优先级进程长期无法运行甚至引发系统卡顿、无响应。原本的-20到19对应PRI的60到100这类范围已经能满足绝大多数场景的优先级区分需求高频调整范围属于过度操作反而会破坏系统原本的调度平衡。修改优先级范围通常需要特殊权限比如root高频操作不仅麻烦还容易因误操作设置极端范围造成不可逆的系统问题。实时操作系统与分时操作系统实时操作系统RTOS: 核心是任务响应的时间确定性——对任何任务请求系统必须在预先规定的“死线”确定时间通常毫秒/微秒级内完成响应和处理绝不能超时。它的目标不是追求资源利用率最大化而是确保高优先级任务的实时执行哪怕牺牲部分非关键任务的资源。分时操作系统 : 核心是多任务/多用户的公平性——将CPU时间分割成固定的“时间片”轮流分配给多个并发任务或用户让每个任务都能“分时”使用CPU。它的目标是让多个任务看起来“同时运行”保证资源分配的公平性对响应时间没有严格的确定性要求延迟可能因任务数量变化而波动。七、进程之间的关系进程具有竞争性,独立性,并行性和并发性竞争性系统进程数目众多几十成百上千都有可能而CPU资源只有少量甚至只有一个所以进程之间是具有竞争属性的。为了更高效完成任务合理竞争资源于是便有了优先级独立性多进程运行需要独享各种资源多进程运行期间互不干扰并行在同一时间内多个进程在多个CPU下分别同时进行运行这称之为并行并发多个进程在一个CPU下采用进程快速切换的方式在一段时间内让多个进程都得以推进称为并发有个疑问,既然进程之间具有独立性,那么在fork之后父子进程共享代码和数据,那父子进程之间又是如何保持独立性的呢?fork创建的父子进程在逻辑上是独立的但默认共享代码段采用写时拷贝机制而数据段则是表面共享实际独立。其独立性保障机制如下核心机制写时拷贝(COW)fork调用后父子进程的代码段、数据段、堆栈等内存区域会先共享物理内存仅复制页表而不复制实际数据提高效率当任一进程尝试修改数据时系统会立即为该进程复制独立的物理内存页确保修改操作不会影响另一个进程从而维护数据独立性独立资源除内存外以下资源也是独立的进程ID文件描述符表共享文件描述符但偏移量独立信号处理函数默认继承但可独立修改资源限制关键点fork后的共享是高效的临时性共享旨在节省内存写时复制机制与独立进程资源共同确保父子进程的逻辑独立性表面上看似共享实际运行时互不干扰进程之间如何实现并发呢?并发指的是多个进程在同一个CPU下采用切换进程的方式在一段时间内让多个进程都得以推进称为并发进程的并发是通过操作系统的“时间片调度进程切换”机制实现的在单CPU环境下系统把CPU时间分割成短时间片轮流分配给多个进程每个进程只运行一个时间片时间到了就触发进程切换保存当前进程的上下文、加载下一个进程的上下文让多个进程在“一段时间内”交替执行——从用户视角看就像多个进程在同时运行。如何实现进程之间的切换?切换触发切换由特定事件启动常见情况有时间片耗尽系统定时器中断触发、高优先级进程进入就绪态、进程因等待I/O操作主动阻塞、硬件中断如键盘输入以及进程执行完毕或异常终止等。调度器选定目标进程触发事件发生后内核中的调度器会暂停当前流程从就绪队列里依据调度算法如优先级调度、时间片轮转挑选下一个要运行的进程同时标记当前进程状态为就绪或阻塞态。保存当前进程完整上下文操作系统会把当前进程的上下文全量存入其PCB进程控制块。一方面保存硬件上下文包括通用寄存器、程序计数器、栈指针等通过 push 指令压入内核栈再同步到PCB若用到浮点指令还会保存浮点寄存器数据。另一方面保存软件上下文像进程的虚拟内存映射信息、打开的文件列表、信号处理配置等也会更新到PCB中。切换地址空间这是进程切换的关键步骤。每个进程有独立虚拟地址空间切换时内核会将新进程页全局目录的物理地址加载到对应CPU寄存器如ARM64架构的 TTBR0_EL1 同时更新CR3寄存器切换页表让CPU后续访问的是新进程的虚拟内存地址。此外还会处理TLB转换后备缓冲器避免旧进程地址映射干扰部分系统用PCID技术减少TLB全量刷新的开销。恢复目标进程上下文从新进程的PCB中读取之前保存的信息。先将硬件上下文寄存器值、程序计数器等重新加载到CPU对应寄存器再切换到新进程的内核栈同时恢复其软件上下文比如重新关联打开的文件、信号处理规则等运行环境配置。跳转执行新进程程序计数器会指向新进程上次暂停时的下一条指令地址CPU自此开始执行新进程新进程状态也被标记为运行态整个切换流程完成。即CPU寄存器中保存的是进程需要访问或修改的数据即进程的相关数据CPU寄存器里保存的是进程的临时数据我们把这些进程的临时数据成为进程的上下文寄存器有很多例如通用寄存器eaxebxecxedx栈帧ebpespeip状态寄存器status八、总结本文系统介绍了操作系统中的进程状态及其管理机制。主要内容包括1. 进程三大核心状态运行R、阻塞S/D、挂起状态及其转换关系2. Linux特有的进程状态实现如深度睡眠D、僵尸Z等特殊状态3. 进程优先级管理PRI/NI和调度机制4. 进程间关系竞争/独立/并发及实现原理5. 特殊进程僵尸/孤儿的产生原因和处理方法。重点阐述了进程状态转换与资源管理的关系以及Linux如何通过调度队列、写时复制等技术实现进程的高效管理。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2419712.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!