【Linux Kernel 6.1 代码剖析】- 进程管理概论
目录
进程与线程的概念(内核线程和用户线程)
进程的3种基本状态
引入挂起后的7种基本状态
Linux 内核6.1 - 进程的8种详细状态
进程控制块 PCB
SMP 架构
进程与线程的概念(内核线程和用户线程)
进程是正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源,比如说CPU(寄存器),IO,内存,网络资源等。每个进程在内核中都有一个进程控制块(PCB)来维护进程的基本信息和运行状态。
线程是系统调度的基本单位,一个进程中可以有多个线程,它们共享进程资源。
从Linux 的角度上也可以这么理解线程和进程:
进程拥有专用的系统堆栈空间,有独立的用户空间,如果一个进程需要和其他的进程共享用户空间,那这个进程就可以被称为用户线程, 如果一个进程完全没有用户空间,那就可以称它为“内核线程”。
进程的3种基本状态
就绪(Ready)状态:
通过Ready我们可以看到,处于此状态的进程已经处于准备好要运行了,此时进程已经分配好除CPU外的所有必要资源,只需获得CPU便可立即执行。
用一句话来描述处于就绪态的进程:万事俱备,只欠CPU。
另外还有一个就绪队列的概念,处于就绪态的进程都在此队列中,等待着调度程序的调度(分配CPU)。
执行(Running)状态:
Running,运行中的,处于此状态的进程是已经获得CPU并且正在执行中。
对于这一状态,在单CPU OS中,同一时刻只能有一个进程处于此状态,而在多CPU OS中,则可以有多个(不超过CPU的数量)进程同时处于执行状态。
阻塞(Block)状态:
阻塞,百度释义为:障碍而不能通过,无法畅通。处于此状态的进程是因为在执行的过程中由于发生某种需要等待的事件(I/O请求、申请缓存失败、等待接收数据等),而暂时无法继续执行。
我们知道在多道批处理系统中,此时需要进程把处理机释放,并选取新的就绪进程执行。与就绪队列相对应的,处于阻塞状态的进程都在阻塞队列中,某些OS中,出于提高效率的目的,根据阻塞的原因,会有多个阻塞队列。
引入挂起后的7种基本状态
被挂起的原因
为了系统和用户观察和分析进程的需要,还引入了一个对进程的重要操作—挂起(Suspend),当对某个进程执行此操作是时,该进程将会被挂起,此时意味着该进程需要释放内存,调至到硬盘中(外存),也意味着此时该进程处于静止状态,无法接受调度或者执行。当该进程被执行激活(Active)操作时,才可将该进程从外存中重新导入到内存中。
- 终端用户的请求:当终端用户在自己的程序运行期间发现有可疑问题时,希望暂停自己的程序的运行,使之停止下来,以便用户研究其执行情况或对程序进行修改;
- 父进程的请求:有时父进程希望挂起自己的某个子进程,以便考察和修改子进程,或者协调各子进程间的活动;
- 负荷调节的需要:当实时系统中的工作负荷较重,已可能影响到对实时任务的控制时,可由系统把一些不重要的进程挂起,以保证系统能正常运行;
- 操作系统的需要:操作系统有时希望挂起某些进程,以便检查运行中的资源使用情况或进行记账。
新增的4种状态
- 活动就绪(Readya):等价于就绪态,此时进程可以接受调度,获得处理机后可直接转为执行态;
- 静止就绪(Readys):进程被调至外存,无法接受调度;
- 活动阻塞(Blockeda):等价于阻塞态,当等待的事件发生后可以由活动阻塞变为活动就绪状态;
- 静止阻塞(Blockeds):进程仍然可以等待事件,当事件发生后,状态由静止阻塞变为静止就绪状态。
Linux 内核6.1 - 进程的8种详细状态
运行状态(TASK_RUNNING):
进程当前正在运行, 或者正在运行队列中等待调度。
可中断的阻塞(睡眠)状态(TASK_INTERUPTIBLE):
进程处于阻塞(睡眠)状态,正在等待某些事件发生或能够占用某些资源。 处在这种状态下的进程可以被信号中断。接收到信号或被显式的唤醒呼叫(如调用wake_up系列宏: wake_up 、wake_up_interruptible 等) 唤醒之后, 进程将转变为 TASK_RUNNING 状态。
不可中断的阻塞(睡眠)状态(TASK_UNINTERUPTIBLE):
此进程状态类似于可中断的阻塞状态, 只是它不会处理信号, 把信号传递到这种状态下的进程不能改变它的状态。 在一些特定的情况下(进程必须等待, 直到某些不能被中断的事件发生), 这种状态是很有用的。 只有在它所等待的事件发生时, 进程才被显式的唤醒呼叫唤醒。
这种状态一般也被称为磁盘睡眠状态,通常在磁盘写入的时候发生。
可终止的阻塞(睡眠)状态(TASK_KILLABLE):
这是Linux 内核 2.6.25 引入了一种新的进程状 态 。 状 态 的 运 行 机 制 类 似 于不可中断的阻塞状态, 只不过处在该状态下的进程可以响应致命信号。
它可以替代有效但可能无法终止的不可中断的阻塞状态, 以及易于唤醒但安全性欠佳的可中断的阻塞状态。
暂停状态(TASK_STOPPED):
进程的执行被暂停,当进程收到 SIGSTOP、SIGTSTP、SIGTTIN、 SIGTTOU 等信号时, 就会进入暂停状态。
跟踪状态(TASK_TRACED):
进程的执行被调试器暂停。 当一个进程被另一个进程监控时(如调试器使用 ptrace()系统调用监控测试程序), 任何信号都可以把这个进程置于跟踪状态。
僵尸状态(EXIT_ZOMBIE):
进程运行结束, 父进程尚未使用 wait 函数族(如使用 waitpid()函数) 等系统调用来“收尸”, 即等待父进程销毁它。 处在该状态下的进程“尸体” 已经放弃了几乎所有的内存空间, 没有任何可执行代码, 也不能被调度, 仅仅在进程列表中保留一个位置, 记载该进程的退出状态等信息供其他进程收集。
僵尸撤销状态(EXIT_DEAD):
这是最终状态, 父进程调用 wait 函数族“收尸”后, 进程彻底由系统删除。
进程控制块 PCB
包含信息
- 进程标识符:用于唯一地标识进程。一个进程通常包含两种标识符:外部标识符和内部标识符。外部标识符一般由创建者(用户)提供,用来方便记忆;内部标识是为了方便OS对进程的使用,通常是Linux系统中查看到的Pid;
- CPU状态:CPU状态信息也称处理机的上下文,主要由CPU的各种寄存器(通用寄存器、指令计数器、程序状态字PSW、用户栈指针)中的内容组成的。进程在执行的过程中,正在处理的许多信息都放在寄存器中,如果需要发生切换,这些信息就需要保存在该进程的PCB中,以便可以再次执行时可以快速的恢复CPU的状态;
- 进程调度信息:在OS进行调度(从就绪队列中选取进程分配CPU)时,必须要知道进程的状态和相关的调度信息,主要包括:进程状态、优先级、进程调度所需的其他信息(等待时间、已经执行的时间等)、事件(等待发生的时间,即阻塞的原因);
- 进程的控制信息:只用于进程控制所必需的信息,主要包括:程序和数据的地址(所在内存或外存的首地址)、进程同步和通信机制、资源清单(进程运行期间所需的全部资源,CPU除外,还有一张已经分配给该进程的资源清单,主要用于避免死锁)、链接指针(指向本PCB所在队列中的下一个进程的PCB的首地址)。
作用
- 作为独立运行基本单位的标志:当一个程序(包含数据)配置了PCB后,就表示他成为了一个进程,一个能在多道程序环境下独立运行并合法的一个基本单位,也就具有了取得OS服务的权利。这也是为什么当系统创建一个进程时,需要为之创建一个PCB,并与进程一对一的绑定,系统是根据PCB来感知进程的,PCB被撤销归还给OS时,进程也就随之消亡了;
- 能实现间断运行的方式:这也是进程的一个基本特性—异步性,因为PCB中保存着进程在CPU上执行时的上下文信息,因此在进程再次被调度执行时,可以快速的恢复其CPU的现场信息。
- 提供进程管理所需要的信息:在进程的整个生命周期中,OS都是根据PCB实施对进程的控制和管理。
- 提供进程调度所需要的信息:OS根据PCB中存储的进程的状态信息来将继承插入到对应的队列中,并根据PCB中保存的优先级、等待事件等来进行调度的;
- 实现与其他进程的同步与通信:PCB中具有用与实现进程通信的区域或通信队列指针等。
SMP 架构
概念
对称多处理器结构 , 英文名称为 " Symmetrical Multi-Processing " , 简称 SMP ;
SMP 又称为 UMA , 全称 " Uniform Memory Access " , 中文名称 " 统一内存访问架构 " ;
在 " 对称多处理器结构 " 的 系统中 , 所有的 CPU 处理器 的 地位 都是 平等的 , 一般指的是 服务器 设备上 , 运行的 多个 CPU , 没有 主次/从属 关系 , 都是平等的 ;
这些处理器 共享 所有的设备资源 , 所有的资源 对 处理器 具有相同的 可访问性 , 如 : 磁盘 , 内存 , 总线 等 ; 多个 CPU 处理器 共享相同的物理内存 , 每个 CPU 访问相同的物理地址 , 所消耗的时间是相同的 ;
优缺点
优点 : 避免了 结构障碍 , 其最大的特点是 所有的资源共享 ;
缺点 : SMP 架构的系统 , 扩展能力有限 , 有瓶颈限制 ;
如 : 内存瓶颈限制 , 每个 CPU 处理器必须通过 相同的总线 访问 相同的内存资源 , 如果 CPU 数量不断增加 , 使用同一条总线 , 就会导致 内存访问冲突 ; 这样就降低了 CPU 的性能 ;
通过实践证明 , SMP 架构的系统 , 使用 2 2 2 ~ 4 4 4 个 CPU , 可以达到利用率最高 , 如果 CPU 再多 , 其利用率就会降低 , 浪费处理器的性能 ;
多处理器下 内核需要做的工作
① 公平共享 : CPU 的负载 , 需要公平地共享 , 不能出现某个 CPU 空闲 , 造成资源浪费 ;
② 可设置进程 与 CPU 亲和性 : 可以为某些类型的 进程 与 指定的 处理器 设置 亲和性 , 可以针对性地匹配 进程 与 处理器 ;
③ 进程迁移 : Linux 内核可以将进程在不同的CPU 处理器之间进行迁移 ;