1、进程的定义

进程(Process) 是正在执行的程序实例。它不仅仅是躺在硬盘上的那一堆代码(程序),而是程序被加载到内存后,开始动态运行的那个“活物”。

为了帮你理解,记住这句话:程序是静态的,进程是动态的。

  • 程序:相当于一本菜谱(躺在书架上,没人动)。

  • 进程:是厨师按照菜谱实际下厨操作的全过程(配菜、点火、翻炒——有状态、有动作)。

严格定义:进程是操作系统进行资源分配和调度的基本单位。

2、进程的组成

一个进程在内存中并不是一团乱麻,而是被操作系统严格划分为六个关键部分。前四个(代码段、数据段、堆、栈)是进程在内存中的“实体内容”,后两个(PCB、内核栈)是操作系统用来“管理”它的数据结构。

(1)程序代码段(Text Segment)—— 进程的“大脑”

存放程序的二进制指令集。这部分在进程中是只读的,防止程序意外或恶意修改自己的代码。多个进程可以共享同一份代码段(比如你打开 10 个 Chrome 标签,它们执行的是同一份 Chrome 代码),从而节省大量内存。

(2)数据段(Data Segment)—— 进程的“账本”

存放程序运行过程中用到的全局变量、静态变量等持久化数据。它内部又分为两部分:

  • 初始化数据段:存放已赋初值的全局变量和静态变量(如 int global = 100;)。

  • 未初始化数据段(BSS 段):存放未赋初值的全局变量和静态变量(如 int global;)。BSS 段在程序加载时会被操作系统清为 0,不占用可执行文件的磁盘空间(只记录大小),加载时才分配内存,这是一种优化手段。

(3)堆(Heap)—— 进程的“自由仓库”

用于动态内存分配。当程序在运行时需要不确定大小的内存(如使用 malloc()new 开辟空间),分配的内存就来自堆。堆的地址从低地址向高地址增长,大小在程序执行过程中可以动态变化(通过 brk 或 mmap 系统调用向操作系统申请扩容或缩容)。

注意:堆的分配释放必须由程序员显式管理(在 C/C++ 中),否则会引发内存泄漏。在 Java/Go 等带垃圾回收(GC)的语言中,由运行时自动回收。

(4)栈(Stack)—— 进程的“临时便签”

用于存储局部变量函数调用的上下文(如返回地址、参数、保存的寄存器值)。每当一个函数被调用,系统就在栈顶压入一个栈帧(Stack Frame);函数返回时,弹出该栈帧。栈基于先进后出(LIFO,Last In First Out)规则操作,地址从高地址向低地址增长。

栈的大小由操作系统(或编译器链接器)预设(通常几 MB),如果递归调用过深或局部变量过大,就会发生栈溢出(Stack Overflow)

(5)进程控制块(PCB,Process Control Block)—— 进程的“身份证”⭐

PCB 是进程存在的唯一标志! 操作系统不是靠名字或路径来感知进程的,而是靠 PCB。它是一个内核数据结构,包含以下关键信息:

信息类别具体内容说明
进程标识信息进程 ID(PID)、父进程 ID(PPID)、用户 ID(UID)身份证号
进程状态信息当前状态(运行/就绪/阻塞等)随时更新
程序计数器(PC)下一条即将执行的指令地址进程被切换时,必须保存现场
寄存器组通用寄存器、堆栈指针、状态字(PSW)等上下文切换的核心
内存管理信息页表指针、内存边界地址、段表虚拟内存到物理内存的映射
调度信息进程优先级、时间片大小、调度队列指针供调度器决策使用
记账信息CPU 已使用时间、I/O 使用量、内存使用峰值系统监控和计费
I/O 状态信息已打开的文件列表、当前使用的 I/O 设备资源管理

进程的 PCB 在进程创建时生成,随进程结束而销毁。PCB 的集合放在内核空间,操作系统通过遍历 PCB 链表来管理系统中的全部进程。

(6)内核栈(Kernel Stack)—— 进程的“内核通道”

每个进程除了拥有用户栈(上面第(4)点提到的栈),在内核空间还拥有自己独立的内核栈。当进程通过系统调用进入内核态,或者发生中断/异常时,CPU 会切换到内核栈来保存临时数据(如系统调用参数、中断嵌套现场)。

为什么需要独立的内核栈?

  • 安全隔离:用户态栈的内容不可信,不能用来处理内核数据。

  • 防止冲突:内核代码执行时可能产生新的栈帧,不能污染用户态栈。

  • 中断嵌套:内核需要足够的空间来保存多层中断的处理上下文。

代码段/数据段/堆/栈 在内存中的典型布局(从低地址到高地址)

高地址  ┌─────────────┐
        │  内核空间    │  (仅内核可访问)
        ├─────────────┤
        │    栈        │  ↓ 向下增长 (局部变量、函数调用)
        ├─────────────┤
        │    堆        │  ↑ 向上增长 (动态分配内存)
        ├─────────────┤
        │  BSS 段      │  (未初始化的全局变量)
        ├─────────────┤
        │  数据段      │  (已初始化的全局变量)
        ├─────────────┤
        │  代码段      │  (只读指令)
        ├─────────────┤
        │  保留区      │  (不可访问,用于捕获空指针)
  低地址 └─────────────┘

提示:堆和栈是相向而行的——堆向上(高地址)涨,栈向下(低地址)涨。如果两者“碰头”,就会发生内存耗尽(Out of Memory),这时 malloc 会返回 NULL 或触发 bad_alloc 异常。

3、进程的组织方式

(1)进程表

操作系统会为每个进程分配一个进程控制块(PCB),并将这些PCB存储在进程表中。进程表是一个包含所有系统中存在进程信息的数据结构,操作系统通过进程表来管理进程的状态和调度。

(2)进程状态

进程的状态是指进程在生命周期中所处的不同阶段。常见的进程状态包括:

  • 新建(New):进程正在创建中。
  • 就绪(Ready):进程已准备好执行,等待CPU调度。
  • 执行(Running):进程正在CPU上执行。
  • 阻塞(Blocked):进程因等待某些事件(如I/O操作)而被暂停。
  • 结束(Terminated):进程执行完成,或因错误而终止。

进程之间的状态转换通常由操作系统根据调度策略和外部事件(如I/O完成)来管理。

(3)父子进程

进程之间有父子关系,父进程可以创建子进程。每个进程都有一个父进程,父进程通过fork()系统调用创建一个子进程,子进程是父进程的副本,但有自己独立的地址空间。

  • 父进程:创建子进程的进程。
  • 子进程:由父进程创建的进程,子进程从父进程继承资源和属性。

(4)进程间通信

进程间通信(IPC,Inter -Process Communication)是指不同进程之间进行数据交换和信号传递的机制。常见的进程间通信方式包括:

  • 管道(Pipe):用于无名的进程间通信。
  • 命名管道(FIFO):一种特殊类型的管道,允许无关的进程之间进行通信。
  • 消息队列:允许进程通过消息进行通信。
  • 共享内存:允许多个进程访问同一块内存区域。
  • 信号量(Semaphore):用于进程间同步和互斥。

4、进程的特征

每个进程在操作系统中都有其独特的特征,这些特征决定了进程如何被操作系统管理和调度。

(1)独立性
每个进程都有独立的地址空间,进程间不能直接访问彼此的内存(除非通过共享内存或IPC机制)。这保证了进程的隔离性和安全性 。

(2)资源拥有者
每个进程可以拥有不同的资源,如CPU时间、内存、I/O设备等。操作系统通过进程调度来合理分配这些资源。

(3)生命周期
进程有一个从创建到终止的生命周期。在生命周期内,进程经历创建、执行、等待、阻塞、终止等不同状态。进程的生命周期由操作系统管理,通常在进程执行完成或发生错误时结束。

(4)并发性
进程的并发性意味着多个进程可以同时执行。在多核CPU上,多个进程可以在不同的核心上并行执行;在单核CPU上,操作系统通过时间片轮转(分时调度)让多个进程看起来同时执行。

(5)调度性

进程由操作系统进行调度,调度的目标是合理地分配CPU时间。操作系统使用各种调度算法(如先来先服务、最短作业优先、轮询调度等)来决定哪个进程何时执行。

(6)进程控制

操作系统通过进程控制来管理进程的执行。控制包括:

  • 进程调度:选择哪个进程获得CPU执行。
  • 进程同步:确保多个进程能够协调地进行共享资源的访问。
  • 进程互斥:避免多个进程同时修改共享数据导致冲突。
  • 进程通信:使得不同进程能够交换数据和信息。

(7)父子进程关系

父进程通过fork()创建子进程,子进程继承了父进程的大部分资源,但有独立的执行上下文。父子进程可以通过wait()exit()进行同步和通信。

5、进程的状态转换

操作系统管理进程的状态,通过状态转换图来描述进程在生命周期中的不同状态及状态间的转换。常见的状态转换如下:

  • 新建(New) → 就绪(Ready):进程创建后准备执行。
  • 就绪(Ready) → 执行(Running):操作系统调度该进程执行。
  • 执行(Running) → 阻塞(Blocked):进程因等待I/O或其他事件被阻塞。
  • 阻塞(Blocked) → 就绪(Ready):等待的事件(如I/O完成)发生,进程恢复就绪。
  • 执行(Running) → 终止(Terminated):进程执行完成,或被强制终止。

总结

  • 进程是操作系统中执行的程序,它是程序的实例,拥有独立的地址空间和资源。
  • 进程的组成包括程序代码、数据、堆、栈和PCB等。
  • 进程的组织方式包括进程表、进程状态、父子进程关系和进程间通信等。
  • 进程的特征包括独立性、资源拥有、生命周期、并发性、调度性、父子进程关系等。

操作系统通过进程管理来确保计算机资源得到合理分配和高效使用,同时保证多任务和多进程环境下的安全性和稳定性。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐