进程
操作系统的背景
批处理系统: 操作系统的最初原型是批处理系统,这种系统允许操作员将所有任务集中起来,由系统自动读取并执行。当一个任务完成后,系统会自动执行下一个任务。然而,批处理系统的主要问题是在任务执行过程中经常需要等待 IO 操作,导致 CPU 频繁空闲。
多道程序设计: 为了解决 CPU 空闲的问题,引入了多道程序设计技术。在这种设计中,内存被划分为多个区域,每个区域存储一个任务的指令和数据。这样,当一个任务等待 IO 操作时,另一个任务可以使用 CPU,从而提高 CPU 的利用率。
分时系统: 除了提高 CPU 利用率,程序员还希望计算机能够快速响应他们的操作。这种需求催生了分时系统的诞生。在分时系统中,操作系统为每个任务分配一定的 CPU 使用时间,确保 CPU 不会长时间沉浸在一个大型任务中,同时也保证了用户操作能够迅速得到响应。
进程的概念: 进程是操作系统正常运转的核心概念。它允许操作系统管理多个任务,确保任务能够高效、有序地执行,同时满足用户对快速响应的需求。
进程存在的意义
操作系统的设计理念: 无论是批处理系统、多道程序设计还是分时操作系统,它们的本质都是实现任务的并发执行,允许多个任务共享 CPU 和计算机的其他资源。
进程的抽象: 为了实现这种并发执行,操作系统将每个正在执行的程序抽象为一个进程。程序员在编写运行在操作系统上层的程序时,会将运行中的程序视为一个独立的进程,并认为每个进程拥有自己的 CPU 和内存资源。这种独立性是虚拟的,由操作系统提供。
操作系统的作用: 操作系统的职责是合理分配和调度真实的底层硬件资源,并为进程提供逻辑上的支持。这使得上层程序开发者可以简化他们的任务,只需关注于每个进程拥有独立的 CPU 和内存资源,而操作系统则负责基于这种设计逻辑去合理地调度和分配复杂的底层硬件资源。
总结: 操作系统通过进程抽象和资源调度,为程序开发者提供了一个简化的开发模型,同时确保了底层硬件资源的有效管理和利用。
进程的定义
从用户的角度来看,进程是一个程序的动态执行过程。是一个把静态程序文件加载进内存、分配资源、执行、调度和消亡的过程。
从操作系统的角度来看,进程是资源分配的基本单位。 进程需要占用 CPU 资源以执行程序指令;而除了需要占用 CPU 资源以外,进程还需要占据存储资源来保存状态。进程需要保存的内容包括数据段、代码段、堆、栈以及其他内存空间,进程也需要占用资源管理打开的文件、挂起的信号、内核内部数据、处理器状态、存在内存映射的内存空间以及执行线程,而执行线程的信息则包含程序计数器、栈和寄存器状态。
综上所述,所谓进程,就是处于执行期的程序和相关资源的总称。故即便通过同一份静态程序, 也可以启动多个不同的进程。
进程的三种形态
进程的三种基本状态:执行态、就绪态和阻塞态。
- 执行态:处于执行态的进程正在运行,这意味着它正在使用 CPU 资源执行程序指令。
- 就绪态:就绪态的进程已经满足了所有执行条件,但它尚未被分配到 CPU 的处理时间。这些进程位于就绪队列中,等待被调度程序选中以获得 CPU 时间。
- 阻塞态:在阻塞态的进程不能使用 CPU,这通常是因为它们正在等待某些事件发生,如 I/O 操作完成、信号量可用或其他条件得到满足。
此外,使用 ps
命令可以观察到进程状态的更细致划分,这些状态提供了关于进程当前状态的额外信息。
进程调度
操作系统通过进程机制实现在相同时间段内完成多个任务。尽管在任何给定时刻,物理 CPU 只能执行一个进程,但操作系统通过时间分片和调度策略,给每个进程分配 CPU 时间。这使得每个进程都感觉好像它独占 CPU 资源,即拥有虚拟 CPU。从用户视角看,多个进程在一段时间内似乎是并发执行的。
操作系统中的调度器负责根据预定的调度策略和优先级为各进程分配 CPU 时间。时间片是进程占用 CPU 时间的基本单位。当进程不需要使用 CPU 资源时,调度器会夺取 CPU 控制权,并迅速切换到另一个进程。这种切换对用户程序的设计是透明的,对用户来说,这种快速的进程切换使得进程看起来像是在同时运行。
这种并发执行的实现依赖于操作系统的调度能力,它能够在极短的时间内完成进程切换,使用户几乎无法察觉到进程之间的切换,从而实现了高效的多任务处理。
并发和并行
并发和并行是两个描述多任务执行的重要概念,它们在多任务操作系统中扮演着关键角色。
- 并发(Concurrency): 并发是指在一段时间内,多个进程或线程似乎同时运行。这种“同时”可能是指在不同的时间点上,这些进程或线程被操作系统调度执行。并发可以通过时间分片实现,操作系统通过快速切换任务,让用户感觉到多个任务在同时进行。
- 并行(Parallelism): 并行则是指在某一时刻,多个进程或线程真正地同时运行。这通常需要多核处理器或多台计算机的支持,每个核心或计算机可以独立执行一个任务。并行执行可以显著提高处理速度和效率,因为它允许多个任务同时进行计算。
区别:
- 并发不一定意味着真正的同时执行,它可能只是在快速地在多个任务之间切换。
- 并行则要求系统具有同时处理多个任务的能力,这通常依赖于硬件资源。
应用场景:
- 并发适用于单核处理器或在需要处理多个任务时,通过操作系统调度来实现任务的快速切换。
- 并行适用于多核处理器或分布式系统,可以充分利用硬件资源,实现真正的同时执行。
常见通用调度算法
- 先来先服务(FCFS, First-Come First-Served):
- 这是一种最简单的调度算法,按照进程到达就绪队列的顺序进行调度。
- 轮转调度(RR, Round Robin):
- 属于时间分片调度算法,所有就绪状态的进程被组织在一个循环队列中。每个进程分配一个固定时间段,按顺序轮流使用 CPU 一定时间。如果进程在其时间片结束时还没有完成,它将被放回队列的末尾。
- 优先级调度:
- 每个进程都被分配一个优先级,CPU 首先分配给具有最高优先级的进程。优先级调度可以是抢占式的或非抢占式的。在抢占式优先级调度中,如果一个高优先级的进程变为就绪状态,它可以立即取代当前正在执行的低优先级进程。
- 最短作业优先(SJF, Shortest Job First):
- 选择估计运行时间最短的进程并首先执行它,以减少平均等待时间。
- 最短剩余时间优先(SRTF, Shortest Remaining Time First):
- 这是最短作业优先的抢占式版本,选择剩余时间最短的进程来执行。
现代操作系统的调度策略:
- 现代通用操作系统通常使用综合性的调度算法,基于多种调度策略,而不是单一策略。
- 不同操作系统在调度策略的具体实现上存在诸多细节上的差异。
多核心 CPU
多核心 CPU 技术实现了真正的并行处理能力,这与单核心 CPU 所采用的时间分片技术有本质区别。单核心 CPU 虽然可以通过快速切换任务来模拟多任务并行,但在任何给定时刻,它实际上只能执行一个任务。相比之下,多核心 CPU 的每个核心能够独立处理不同的进程或线程,从而在处理多任务时显著提高整体的计算能力和效率。
在现代操作系统中,进程在 CPU 上的切换涉及到保存和恢复进程的运行状态,这个过程需要记录进程的多个关键信息,如状态、程序计数器、CPU 寄存器、内存管理信息、账号信息和 I/O 状态等。这些信息被统称为进程控制块(PCB)。每个进程都有其对应的 PCB,该 PCB 在整个进程的生命周期中存在,并在进程终止时被销毁。
操作系统的调度器负责管理多核 CPU 上的进程调度。调度器需要决定哪个进程在哪个核心上运行,并在需要时在核心之间迁移进程。当进程从一个核心迁移到另一个核心时,其 PCB 中的信息将用于在新的核心上恢复进程状态,确保进程能够从上次停止的地方继续执行。这种调度和管理机制对于确保系统资源的高效利用和进程的平滑运行至关重要。
进程的抢占
Linux 内核中的调度器是一个关键组件,负责管理和调度进程的执行。调度器的主要任务是从一组处于可执行状态的进程中选择一个进程来执行。Linux 操作系统支持抢占式多任务模式,这意味着调度器可以决定在任何时候停止一个进程的运行,并让另一个进程开始执行。这种行为被称为进程的抢占。
以下是一些常见的导致 CPU 抢占的情况:
- 时间片用尽:在使用基于时间片的调度策略时,当一个进程的时间片用完,调度器会选择另一个进程来运行。这是确保系统资源公平分配的一种机制。
- 更高优先级的进程变为可运行状态:如果一个具有更高优先级的进程,尤其是实时进程,变为可运行状态,它可能会抢占当前正在运行的较低优先级的进程。这有助于确保高优先级任务能够及时得到处理。
- 中断和系统调用:在处理中断或完成系统调用后,内核可能会重新评估当前运行的进程,并决定是否需要进行上下文切换。这有助于响应外部事件和系统请求,同时保持系统的响应性和稳定性。
Linux 内核的调度器通过这些机制确保了多任务环境中的高效和公平的资源分配,同时也提高了系统的响应性和性能。
时间片轮转法
时间片轮转法 是一种经典的进程调度方法,其核心思想是为每个进程分配一个固定的时间片段,以实现 CPU 时间的公平分配。
- 就绪队列:调度器将所有可运行的进程组织在一个就绪队列中,等待被调度执行。
- 调度周期:调度器设定一个固定的时间周期,称为调度周期或调度延迟。在每个调度周期开始时,调度器为就绪队列中的每个进程分配时间片。
- 时间片分配:每个进程获得的时间片长度与其优先级相关。高优先级的进程可能会获得更长的时间片。
- 时间片耗尽:当一个进程的时间片耗尽时,如果它仍在运行状态,调度器将中断其执行,让就绪队列中的下一个进程开始执行。耗尽时间片的进程被放回就绪队列,并根据其优先级重新分配时间片。
- I/O 操作等待:如果进程在执行过程中需要等待 I/O 操作,即使其时间片未用尽,也会被放入等待/阻塞队列,并将其状态设置为等待/阻塞状态。
- 唤醒进程:当等待的 I/O 操作完成,进程被唤醒,调度器根据其优先级重新分配时间片,并将其插入到就绪队列中,等待再次执行。
- 进程终止:当进程执行完毕,它会从就绪队列中移除,结束其生命周期。
时间片轮转法通过这种方式确保了所有进程都能获得公平的 CPU 时间分配,同时也提高了系统的响应性。
Linux 的调度策略
Linux 操作系统采用了多种进程调度策略,用以适应不同的使用场景和需求。每个进程都有一个关联的调度策略和调度优先级,这些信息存储在 task_struct
结构中,并可以被修改。以下是 Linux 主要的进程调度策略:
- 完全公平调度器(Completely Fair Scheduler, CFS):也称为普通调度策略(SCHED_OTHER)。这是 Linux 的默认调度策略,适用于大多数无特殊要求的任务,例如用户界面、文档编辑、互联网浏览等,以及不需要严格时间约束的后台任务和系统服务。
- 实时调度策略(Real-time Scheduling Policies):实时调度策略分为两种模式:
- 先到先服务(First-In-First-Out, FIFO):适用于对任务执行顺序有严格要求的场景。
- 轮转调度(Round Robin, RR):适用于需要快速且公平响应的任务,如硬件控制和实时数据处理。
实时调度策略适用于对响应时间有严格要求和约束的任务,这些任务需要快速响应,以保证系统的实时性能。
在 Linux 操作系统中,实时调度策略的进程具有比完全公平调度器(CFS)策略下的进程更高的执行优先级。例如,在 Ubuntu 系统中,共有 140 个优先级别,范围从-40 到 99,其中-40 代表最高优先级。在这 140 个优先级中,-40 到 59 这 100 个优先级是分配给实时调度策略(如 FIFO 和 RR)的,而 60 到 99 这 40 个优先级是为非实时策略进程保留的,我们可以认为这些是为 CFS 策略/OTHER 保留的。
用户可以通过执行 ps -elf
命令来查看进程的优先级,其中 PRI 列显示了进程的优先级。在 Linux 系统中,特别是在单核心 CPU 的情况下,只有当实时调度策略的进程处于非就绪态或非执行态时,非实时策略的进程才有机会占用 CPU 运行。否则,实时调度策略的进程将会持续占用 CPU。
这种优先级的设计确保了实时任务能够获得足够的 CPU 资源,以满足它们对响应时间的严格要求。同时这也意味着在高优先级的实时任务存在时,低优先级的非实时任务可能会遭受到调度延迟。
完全公平调度算法
尽管 CFS(Completely Fair Scheduler)在概念上与时间片轮转调度法存在诸多不同,它们的宏观表现却颇为相似。CFS 摒弃了固定时间片的分配方式,而是为每个进程赋予一个“虚拟运行时间”值,这一指标反映了进程的相对运行时长。
CFS 采用红黑树数据结构来追踪所有处于可运行状态的进程。该树结构根据进程的“虚拟运行时间”进行排序,从而使得 CFS 能够迅速识别并选择具有最小“虚拟运行时间”的进程作为下一个运行目标。
CFS 能够动态地调整各个进程的时间片大小。例如,对于交互式进程或前端应用程序,它们会获得比批处理进程或后台任务更短的时间片,以提高响应性。
CFS 特别倾向于优先调度那些长时间处于睡眠状态的进程。这意味着,当一个进程完成等待事件(例如用户输入或文件 I/O 操作)并重新唤醒时,它的“虚拟运行时间”相对较低,因此更有可能被迅速选中进行调度。
此外,CFS 还承担着在多核心 CPU 系统中实现负载均衡的任务,尽管这一功能并不完美,有时会导致对特定 CPU 核心的偏好,但它努力确保各个 CPU 核心之间的工作负载得到均衡分配。
实时调度策略
FIFO 调度策略
SCHED_FIFO 调度策略摒弃了时间片的概念。在这种策略下,一旦一个进程开始执行,它将持续占用 CPU,直到它主动释放或被一个更高优先级的进程抢占。如果当前 CPU 正在执行一个 FIFO 策略的进程,而此时一个具有更高优先级的 FIFO 进程变为就绪状态,它将立即抢占 CPU。这确保了在所有可运行的进程中,具有最高优先级的进程总是获得优先执行权。
值得注意的是,”先来先服务” 的原则在 FIFO 中仅适用于相同优先级内的进程调度。假设有多个具有相同优先级的 FIFO 策略进程同时进入就绪状态,它们将按照到达的顺序被组织进就绪队列。实际上,系统底层对于不同优先级的进程分别维护了不同的队列,每个优先级对应一个独立的队列。
RR 调度策略
SCHED_RR 调度策略引入了时间片的概念,确保相同优先级的进程能够公平地分配到相同大小的时间片。在 SCHED_RR 策略下,一个进程在执行过程中,一旦其时间片耗尽,它将被重新放回就绪队列的末尾,等待下一个时间片的到来。
SCHED_RR 策略是在 SCHED_FIFO 的基础上发展而来,它允许相同优先级的进程通过时间片轮转机制来共享 CPU 资源。尽管如此,SCHED_RR 仍然保留了实时调度的核心特性,包括高优先级进程对低优先级进程的抢占,以及高优先级进程的优先执行。
实时进程调度
实时进程并不采用 CFS 和红黑树进行调度,而是根据它们的实时调度策略和优先级,在不同的队列中进行管理。这种管理方式确保了实时进程能够迅速获得响应,满足实时系统对响应时间的严格要求。
进程默认的优先级别
在操作系统中,进程的优先级是一个关键因素,它决定了进程获得 CPU 时间的顺序。对于大多数我们日常启动的进程,它们遵循的是普通调度策略,即 CFS(Completely Fair Scheduler)。CFS 策略下,进程的优先级别范围被设定在 [60, 99] 之间。
Linux 系统在普通调度策略中定义了一个“中间位置”作为优先级控制的基准点。这个基准点,也就是我们通常创建、操作或接触到的进程的默认优先级,被设定为 80。这意味着,在没有进行任何优先级调整的情况下,新启动的进程将自动获得这一默认优先级,从而在 CFS 调度策略下进行公平的 CPU 时间分配。
nice 值调整
在操作系统中,进程的优先级可以通过 nice 值进行调整,nice 值的范围从-20 到 19。nice 值的正值意味着降低进程的优先级,而负值则表示提升进程的优先级。
例如,对于一个遵循普通调度策略(CFS)的进程,其默认的优先级设置为 80,nice 值初始为 0。如果需要提高该进程的执行优先级,可以通过调整 nice 值来实现。例如,通过系统命令将进程的 nice 值设置为-20,那么该进程的新优先级将是 80 – 20 = 60,从而提高了其执行优先级。请注意,优先级值越小,表示进程的优先级越高。
进程的 nice 值可以通过 ps -elf
命令进行查看,其中 “NI” 列显示了 nice 值。
值得注意的是,nice 值仅作为进程 “虚拟运行时间” 的一个初始参考。随着进程的运行,其虚拟运行时间会动态变化。通常情况下,具有较高权重(即较低 nice 值)的进程,其虚拟运行时间增加得较慢;而权重较低(即较高 nice 值)的进程,其虚拟运行时间增加得较快。
虚拟内存
每个进程在逻辑上不仅独占 CPU 资源,而且也拥有一片连续的虚拟内存空间。这种内存空间虽然不是物理上连续的,但通过虚拟内存技术,每个进程都被赋予了一个逻辑上连续且独立的内存地址空间。
虚拟内存技术通过虚拟地址到物理地址的映射机制,为每个进程提供了一个地址连续的内存空间。这种映射关系允许操作系统将物理内存和磁盘空间组合成一个更大的、逻辑上连续的内存空间。当虚拟地址转换后在物理内存中未命中时,系统将从磁盘加载所需的数据到空闲的页框中,或者在内存不足时进行页面置换。这样,虚拟内存机制不仅打破了物理内存的限制,扩展了可用的逻辑内存大小,而且还利用硬盘空间作为额外的存储容器。
此外,虚拟内存机制为每个进程提供了一个独立的地址空间,从而在逻辑上实现了进程间的内存隔离。这种隔离增加了操作系统的稳定性和安全性,因为进程间的内存空间不再相互干扰,降低了潜在的错误和安全风险。
虚拟内存的大小
在理论上,32 位操作系统能够为每个进程提供 4GB 的虚拟内存空间。这是因为 32 位 CPU 的地址总线宽度为 32 位,允许其寻址范围达到 2^{32} 字节,即 4GB。然而,实际上可用的虚拟内存大小可能会受到操作系统设置的限制,例如通过 ulimit -a
命令查看的系统资源限制。
对于 64 位系统,理论上的虚拟内存空间达到了 2^{64} 字节,这远远超过了目前任何实际应用的需求。然而,实际可用的虚拟内存空间同样受限于操作系统的配置和物理硬件的能力。
怎么通过虚拟内存访问实际物理空间
操作系统通过维护每个进程的页表来实现虚拟内存到物理内存的映射。页表中包含了虚拟页到物理页框的映射信息,这使得操作系统能够追踪每个进程的内存使用情况。
CPU 内部的 TLB(Translation Lookaside Buffer)是一个高速缓存,用于存储虚拟地址到物理地址的映射关系。当进程访问虚拟地址时,CPU 首先检查 TLB 以寻找映射信息。如果 TLB 中存在该映射,CPU 将直接使用该映射将虚拟地址转换为物理地址,并访问相应的内存单元。如果 TLB 中未找到映射,即发生 TLB 缺失,CPU 将查询内存中的页表以获取物理地址。一旦找到,CPU 不仅会使用该物理地址访问内存单元,还会将这个新的映射加载到 TLB 中,以加快未来对该地址的访问速度。
此外,在访问物理地址对应的内存单元时,系统会首先检查 Cache。如果数据在 Cache 中,则直接命中并访问。如果 Cache 中没有所需数据,系统将从物理内存中加载数据到 Cache。如果物理内存中也没有该数据,系统将不得不从磁盘加载数据到内存,然后再从内存加载到 Cache,以便进行访问。
进程模式
在操作系统中,进程的执行状态通常分为两种基本模式:用户态(用户模式进程)和内核态(内核模式进程)。
用户模式(User Mode)
- 在用户模式下运行的进程执行的是应用程序代码。
- 此模式下的进程受限于对硬件资源的直接访问权限,无法执行可能影响系统稳定性的操作。例如,当应用程序需要进行文件读写或内存分配等操作时,它必须通过系统调用(System Call)请求操作系统提供服务。在这一过程中,进程从用户模式切换到内核模式。
内核模式(Kernel Mode)
- 内核模式下的进程执行的是操作系统内核的代码。
- 在此模式下,进程具有执行所有 CPU 指令和访问系统所有内存地址的权限。这是因为进程现在作为操作系统的一部分,负责执行系统级任务,如处理系统调用或管理硬件设备。
- 完成内核模式下的操作后,进程将切换回用户模式,继续执行用户级的应用代码。
进程在其生命周期中可能多次在用户模式和内核模式之间切换,这通常发生在进行系统调用时。这种模式切换机制不仅确保了操作系统的安全性和稳定性,防止用户应用直接执行可能危害系统的操作,同时也保证了用户应用的运行环境简单且安全,因为它们无法直接访问敏感资源或执行关键系统操作。
用户态空间和内核态空间
当操作系统创建一个新的进程时,它会为该进程分配两个主要类型的内存空间:用户态空间和内核态空间。
用户态空间分配
- 新进程被赋予一个独立的虚拟地址空间,这个空间是隔离的,确保了进程间内存的独立性。
- 用户态空间包括程序的代码(文本段)、数据段(例如全局变量)、堆(用于动态内存分配)和栈(用于函数调用时的局部变量存储)。
- 操作系统根据可执行文件的指令,将程序代码和数据加载到用户态空间的相应区域,从而设置进程的内存映像。
内核态空间分配
- 每个进程都有一个进程控制块(PCB),它包含了进程状态、程序计数器、CPU 寄存器、内存管理信息、账目信息和 I/O 状态信息等。PCB 位于内核空间。
- 进程在内核态也有自己的栈,用于处理系统调用、中断和异常。这个栈在进程进入内核态时用于保存函数参数、局部变量等,以及在进程和线程切换时保存 CPU 状态和寄存器内容。
- 操作系统还会为新进程在内核态空间中创建文件描述符表,用于管理进程打开的文件和其他资源。
内存保护机制
- 操作系统使用内存保护机制确保进程只能访问自己的用户态空间,防止进程间的数据干扰或对内核空间的非法访问。
- 在内核态,进程具有执行低级或硬件级操作的权限。操作系统通过控制进程在需要时才进入内核态,并在操作完成后返回用户态,以降低安全风险。
PCB 块
进程控制块(PCB),也称为 Process Control Block,在操作系统中扮演着至关重要的角色。它是存储有关进程信息的数据结构,每个进程在操作系统中都拥有一个对应的 PCB。PCB 包含了操作系统管理和调度进程所需的所有信息,是实现进程挂起、恢复以及状态保存和恢复的关键。
PCB 通常包含以下类型的信息:
- 进程标识符(PID):每个进程都有一个唯一的 PID,用于在系统中区分不同的进程。
- 进程状态:指示进程的当前状态,包括就绪(Ready)、运行(Running)、等待(Waiting)或终止(Terminated)。
- 程序计数器(PC):存储下一条要执行的指令地址,用于在中断或等待后恢复执行。
- CPU 寄存器:包括累加器、索引寄存器、栈指针等,这些寄存器的值在进程切换时需要被保存和恢复。
- CPU 调度信息:包括进程优先级、调度队列指针等,用于辅助进程调度决策。
- 内存管理信息:如页表或段表的指针,定义进程的虚拟地址空间。
- 会计信息:监控和统计 CPU 使用时间、实际使用时间等。
- I/O 状态信息:管理进程的 I/O 操作,包括分配的 I/O 设备和打开的文件列表。
- 进程控制信息:涉及进程间通信、信号处理、退出状态码等。
PCB 的具体内容和结构可能因操作系统的不同而有所差异,但其核心功能是一致的,即支持操作系统有效地管理和调度进程。进程创建时,操作系统会为其分配 PCB,并在整个生命周期中维护这个结构。进程结束时,PCB 会被操作系统回收。通过 PCB,操作系统能够跟踪所有进程的状态,并在必要时执行上下文切换,确保系统的协调运行。
task_struct
在 Linux 操作系统中,task_struct
是一个核心的数据结构,它实现了进程控制块(PCB)的功能。尽管在不同的操作系统中,实现进程信息的数据结构可能有不同的名称和形式,但它们的核心目的和功能是一致的:保存和管理操作系统所需的进程信息。
task_struct
是 Linux 内核中关于 PCB 的具体实现,它包含了 Linux 维护进程所需的几乎所有信息。以下是 task_struct
中包含的一些关键信息:
- 进程状态:指示进程的当前状态,如可运行、不可运行、停止等。
- 程序计数器:存储下一条要执行的指令的地址,用于控制程序执行流程。
- 进程标识符:包括进程 ID(PID)和父进程 ID(PPID)等,用于唯一标识进程。
- 进程调度信息:涉及调度策略、优先级等,用于指导内核如何调度进程。
- 内存管理信息:包含页表指针、虚拟内存区域等,定义了进程的内存布局。
- 文件系统信息:如文件描述符数组、当前工作目录等,管理进程与文件系统的交互。
- 信号处理信息:包括信号处理函数、信号掩码等,处理进程接收到的信号。
- CPU-specific context:在上下文切换时,用于保存和恢复 CPU 寄存器的状态。
- 链接信息:提供指向父进程、子进程、同一进程组进程的指针,维护进程间的关联。
task_struct
的设计使得 Linux 内核能够有效地跟踪所有进程的状态,并在进行进程调度时执行必要的上下文切换。这种结构的设计体现了 Linux 内核在进程管理方面的高效性和灵活性。