linux中的进程

进程是什么

1. 进程

进程(Process) 是操作系统中对程序一次运行过程 的抽象描述,是操作系统进行资源分配CPU 调度的基本单位。

从本质上讲,进程不是程序本身,也不是单纯的内存空间,而是:

程序在操作系统管理下的一次动态执行实体

其"动态性"体现在:

  • 有明确的生命周期(创建、运行、阻塞、结束)

  • 有不断变化的执行状态

  • 会被操作系统调度、挂起、唤醒和回收

因此,进程是操作系统为了管理并发执行而引入的一种抽象对象


2. 进程的本质:进程 = 内部数据结构 + 程序代码与数据

结论性表述

从操作系统(尤其是内核)的视角看,可以将进程概括为:

**进程 = 内核中的进程描述数据结构

  • 该进程所运行的程序代码与数据**

这一定义同时涵盖了:

  • 操作系统如何"认识"进程

  • 进程实际"执行"的内容


为什么必须有"内部数据结构"?

操作系统需要对进程进行管理,而管理的前提是:

进程必须是"可描述、可定位、可操作"的对象

为此,操作系统为每个进程维护一组内部数据结构,用于记录和管理该进程的运行环境,例如:

  • 进程的唯一标识(PID)

  • 当前执行状态(运行、就绪、阻塞等)

  • CPU 上下文信息(用于切换)

  • 内存使用情况

  • 打开的文件和其他系统资源

  • 与其他进程的父子关系

这些信息并不属于程序本身,而是操作系统为了管理进程而额外维护的状态

👉 没有这些内部数据结构,操作系统就无法调度、暂停、恢复或终止一个进程

因此,从内核视角看:内部数据结构是"进程存在的前提"


为什么还必须有"程序代码与数据"?

另一方面,如果只有内部数据结构,而没有程序代码与数据:进程将"无事可做",不具备任何计算意义

进程所运行的程序代码与数据包括:可执行程序的指令(代码段),程序使用的全局数据、堆、栈,运行时依赖的共享库

这些内容决定了:进程"要做什么",进程"如何计算"

👉 程序代码与数据赋予进程"行为和功能"


二者的分工与关系

可以从职责上区分这两部分:

组成部分 作用
内部数据结构 操作系统管理进程的依据(调度、资源、状态)
程序代码与数据 进程实际执行的计算内容

二者缺一不可:

  • 只有程序代码,没有内部数据结构 → 程序无法运行

  • 只有内部数据结构,没有程序代码 → 进程没有意义

因此:

进程并不是"程序"
而是"被操作系统管理并正在执行的程序实例"


推论

由"进程 = 内部数据结构 + 程序代码与数据"可以自然推出:

  1. 同一程序可以对应多个进程

    • 代码可以共享

    • 内部数据结构彼此独立

  2. 进程是操作系统的管理对象,而程序不是

    • 操作系统"看见"的是进程

    • 程序只是进程运行的内容之一

  3. 线程本质上是共享代码与数据、但拥有独立执行状态的执行实体

    为后续理解"线程为何被视为轻量级进程"奠定基础


好,这一节正好是**"进程概念 → 内核设计方法论"** 的桥梁。我按你写笔记的方式来,先给结论,再回答「为什么」,最后解释「怎么样」,逻辑是可递进、可展开的。


为什么以及怎么样:先描述,后组织

1 结论性说明

Linux 对进程的管理遵循一个非常核心的设计思想:

先对进程进行完整、统一的描述,
再根据不同的管理需求对这些描述对象进行组织。

也可以概括为:

描述是基础,组织是手段;
描述面向"进程是什么",组织面向"如何管理进程"。


2 为什么要"先描述,后组织"?

操作系统首先需要"认识进程"

操作系统要管理进程,必须先回答一个最基本的问题:

"一个进程在系统中到底由哪些信息构成?"

如果没有统一的描述:无法判断两个进程是否是不同实体,无法保存和恢复进程状态,无法在调度、信号、内存管理等子系统之间共享信息

在操作系统中管理进程之前,内核首先必须解决一个根本问题:进程在内核中到底以什么形式存在。进程并不是一个抽象的概念标签,而是一个需要被保存、被查找、被调度、被切换的实体。为了做到这一点,内核必须为每一个进程建立一个稳定且完整的"描述",用以记录该进程当前所处的状态、所占用的资源以及与系统中其他进程的关系。只有当进程被明确地描述出来,它才可能成为操作系统内部一个可以被操作和管理的对象。

因此,内核必须为每个进程建立一个完整、统一的描述,作为所有管理行为的基础。

👉 描述的本质:定义"进程是什么"


不同管理需求需要不同的"视角"

进程需要被管理的方面是多样的:

  • 调度器关心:哪些进程可以运行?

  • 信号子系统关心:信号该发给谁?

  • 进程控制关心:父子关系如何维护?

  • 用户态工具关心:系统中有哪些进程?

这些问题:关注点不同,使用场景不同,数据访问方式也不同

如果把所有管理逻辑都混进一个"统一结构",会导致:结构臃肿,子系统强耦合,扩展和维护困难

👉 因此,"描述"和"如何使用描述"必须分离


先描述,才能被多次、灵活地组织

一旦每个进程都有了稳定的描述对象:

  • 同一个进程

    → 可以同时参与多种管理结构

  • 不同的子系统

    → 可以用各自最合适的数据结构来管理进程

这种"先描述"的做法,本质上是为了让进程在内核中具有明确而统一的存在形式。无论是调度器、内存管理、信号处理,还是进程控制与回收,所有子系统在面对同一个进程时,都必须基于同一份事实来源。如果没有一个统一的描述,各个子系统就不得不维护各自的进程信息副本,这不仅会造成信息冗余,还会引入一致性和同步上的复杂问题。因此,内核通过集中式的进程描述,将"进程是什么"这一问题先行固定下来。

这使得内核可以做到:

"一个进程,多种组织方式,多种管理视角"


3 "描述"和"组织"各自解决什么问题?

描述解决的问题

描述关注的是单个进程自身的完整性

  • 进程是谁(身份)

  • 在做什么(状态)

  • 用了什么资源(内存、文件)

  • 当前执行到哪里(上下文)

在完成对单个进程的描述之后,内核才进一步考虑如何同时管理大量进程。系统中往往存在成百上千个进程,不同的管理任务对进程的"关注方式"并不相同。有的场景需要根据进程标识快速定位某一个进程,有的场景需要遍历系统中所有进程,有的场景只关心当前可运行的进程集合,还有的场景需要维护进程之间的父子层级关系。这些需求在访问模式、效率要求和结构形式上都存在明显差异,因此不适合用单一的数据结构来统一解决。

👉 描述的目标是:
让一个进程在内核中成为一个"自洽、独立、可保存"的对象


组织解决的问题

组织关注的是进程之间的关系和管理效率

  • 如何快速找到某个进程

  • 如何遍历所有进程

  • 如何选择下一个运行的进程

  • 如何表达进程之间的层级关系

在这一背景下,"后组织"的思想自然产生。内核并不重新定义新的进程对象,而是以已经存在的进程描述为基础,根据不同的管理目的,将这些描述对象组织进不同的数据结构中。这些组织结构并不包含进程的完整信息,而只是保存对进程描述的引用或链接,从而以最低的成本服务于各自的管理需求。同一个进程描述可以同时出现在多种组织结构中,而这些组织方式彼此独立、互不干扰。

👉 组织的目标是:
让大量进程在系统中"可管理、可扩展、高性能"


4 怎么样做到"先描述,后组织"?

每个进程对应一个独立的描述对象

Linux 中的基本做法是:

  • 每创建一个进程

  • 就分配一个独立的进程描述对象

  • 该对象完整记录该进程的所有关键信息

这个描述对象:

  • 生命周期与进程一致

  • 不依附于某一种管理方式

  • 可以被多个子系统同时引用

Linux 在实现"先描述,后组织"这一思想时,核心做法是将进程本身的定义进程的管理方式严格分离。内核在创建进程时,首先为其分配并初始化一个专门用于描述进程的内部对象,该对象完整记录进程的身份、状态、执行上下文以及所占用的系统资源。这一描述对象在进程生命周期内保持唯一性和权威性,内核中所有关于该进程的操作都以它为事实基础,而不会再生成其他等价的进程副本。

👉 这是"先描述"的具体体现


通过"引用"而不是"复制"来组织进程

在此基础上,内核采用的组织方式是:

用不同的数据结构保存对进程描述对象的引用

而不是:

  • 为调度复制一份

  • 为父子关系再建一份

  • 为 PID 管理再建一份

这样做的结果是:

  • 进程信息只有一份真实来源

  • 各种组织结构只关心"如何使用它"

在此基础上,内核并不把管理逻辑直接写死在进程描述之中,而是通过引用和链接的方式,将同一个进程描述对象纳入不同的管理结构。每一种管理需求------例如查找、调度、层级关系维护或资源控制------都有各自合适的数据结构,而这些结构中保存的并不是进程的完整信息,而是指向进程描述对象的关联关系。这样一来,组织结构只负责"如何使用进程",而不负责"进程是什么"。

进程描述在设计时具有高度的稳定性和完整性。它对外暴露的并不是某一种特定用途的视角,而是能够被多个子系统同时理解和使用的通用信息集合。调度器、内存管理子系统和信号子系统虽然关注点不同,但它们都可以基于同一个进程描述读取或更新自己关心的部分,而无需关心该进程在其他管理结构中的存在方式。

👉 这是"后组织"的关键手段


不同组织结构服务于不同目的

同一个进程描述对象,可以同时:

  • 被放入"按 ID 快速查找"的结构中

  • 被挂入"进程层级关系"的结构中

  • 在"调度相关结构"中等待运行

  • 参与资源控制、命名空间等管理结构

这些组织方式之间:

  • 相互独立

  • 互不干扰

  • 但操作的都是同一个进程描述

当进程状态发生变化时,内核通过维护这些组织结构与进程描述之间的一致性来完成管理。例如,一个进程从不可运行状态转为可运行状态时,其描述对象本身并不会改变身份,而只是被相应地加入或移出某些管理结构。进程的"存在"始终由描述对象保证,而进程的"可管理性"则由组织结构动态体现。这使得进程的本质信息与管理策略在实现层面自然分离。

最终,Linux 通过这种方式实现了"一个进程描述,多种组织方式"的结构:进程先以描述对象的形式存在于内核中,再根据需要被挂接到不同的管理体系中。描述决定进程的身份与状态,组织决定进程在系统中的管理路径和处理方式。


5 抽象层面的类比

可以这样理解:

描述进程,类似于定义一个"人"的完整档案

组织进程,则是把这些人:

  • 按家庭关系分组

  • 按部门分组

  • 按权限分组

  • 按当前是否在岗排队

档案只有一份,但组织方式可以有很多种


6 小结

Linux 对进程的管理遵循"先描述,后组织"的思想。

先通过统一的描述对象,完整刻画单个进程的状态和资源;

再根据调度、查找、层级关系等不同需求,

将这些描述对象组织进不同的数据结构中。

这种设计使得进程描述稳定、管理方式灵活,

也是 Linux 内核可扩展、高性能的重要原因之一。


认识pcb(Process Control Block,进程控制块

PCB 里保存了:进程是谁、现在跑到哪了、用什么资源、该怎么调度、和谁有关系。


PCB 里通常包含哪些内容?

PCB(Process Control Block,进程控制块)是操作系统内核用于描述和管理进程的核心数据结构。在 Linux 系统中,PCB 主要由 task_struct 结构体实现。内核并不直接操作"进程"这一抽象概念,而是通过 PCB 来感知进程的存在、状态以及所占用的系统资源,因此可以认为 PCB 是进程在内核中的唯一表示。

1、进程的"身份信息"

PCB 中首先包含进程的基本标识信息,用于唯一确定一个进程及其权限属性。这些信息包括进程 ID、父进程 ID、所属用户和用户组等。通过这些字段,内核能够区分不同进程,并进行权限检查和进程层级管理。


2、进程状态(非常重要)

PCB 还记录了进程的当前状态,用以描述进程在生命周期中所处的阶段。常见的状态包括运行态、就绪态、睡眠态、停止态以及僵尸态。进程状态是进程调度和状态转换的依据,系统工具如 pstop 所显示的进程状态信息,正是来源于 PCB 中的状态字段。

常见状态包括:

  • TASK_RUNNING:正在运行 / 就绪

  • TASK_INTERRUPTIBLE:可中断睡眠

  • TASK_UNINTERRUPTIBLE:不可中断睡眠(如 IO)

  • TASK_STOPPED:被暂停

  • TASK_ZOMBIE:僵尸进程

📌 ps, top 看到的状态,就来自 PCB


3、CPU 相关信息(上下文)

为了支持进程的切换与恢复,PCB 中保存了与 CPU 执行相关的上下文信息。这些内容包括程序计数器、栈指针以及通用寄存器的值。当发生进程切换时,内核会将当前进程的 CPU 上下文保存到其 PCB 中,同时从下一个进程的 PCB 中恢复对应的上下文,从而实现进程在时间上的并发执行。

这部分决定了进程切换是否可行

  • 各种 寄存器的值

    • PC(程序计数器)

    • SP(栈指针)

    • 通用寄存器

  • 内核栈指针

  • 用户栈指针

🔁 进程切换 = 保存旧进程 PCB + 恢复新进程 PCB


4、调度信息

在进程调度方面,PCB 中还包含调度策略和优先级等信息。调度器根据 PCB 中的调度参数决定进程何时获得 CPU 以及能够运行多长时间。Linux 的进程调度机制正是围绕 PCB 中的调度相关字段进行工作的。

  • 调度策略:

    • SCHED_NORMAL

    • SCHED_FIFO

    • SCHED_RR

  • 优先级(priority / nice 值)

  • 时间片信息

  • 调度队列指针

💡 Linux 调度器(CFS)就是围绕 PCB 工作的。


5、内存管理信息

PCB 还维护了进程的内存管理信息,用于描述进程所拥有的虚拟地址空间。通过指向内存描述结构体的指针,PCB 与进程的代码段、数据段、堆和栈等内存区域建立联系。进程创建时,内核会为其建立相应的内存管理结构,并在进程运行期间通过 PCB 进行统一管理。

  • 指向 mm_struct

  • 虚拟地址空间:

    • 代码段

    • 数据段

  • 页表信息

📌 fork() 后父子进程的 PCB 会复制 + 写时拷贝(COW)


6、文件和 IO 相关

在文件与输入输出管理方面,PCB 中保存了进程已打开的文件信息以及文件系统相关的上下文。进程在运行过程中打开的文件描述符表、当前工作目录等内容,都可以通过 PCB 间接访问,这使得操作系统能够正确地进行文件访问控制和资源回收。

  • 打开的文件描述符表(fd table)

  • 当前工作目录(cwd)

  • 根目录(root)

  • 文件系统上下文

比如你写:

复制代码
int fd = open("a.txt", O_RDONLY);

👉 fd 就被记录在 PCB 关联的数据结构里。


7、进程关系(组织结构)

此外,PCB 还记录了进程之间的组织关系。每个进程在 PCB 中都保存了指向父进程和子进程的引用,从而在内核中形成一棵进程树。这种结构为进程的创建、终止以及作业控制提供了基础支持。

  • 父进程

  • 子进程链表

  • 兄弟进程

  • 进程组 ID

  • 会话 ID

📌 shell、后台进程、作业控制都靠这套关系。


8、信号相关

在信号处理方面,PCB 中包含了与信号相关的数据,用于描述进程能够接收哪些信号、当前是否存在未处理的信号以及对应的处理方式。信号机制正是通过修改和检查 PCB 中的相关字段来实现对进程的异步控制。

  • 信号掩码(blocked signals)

  • 待处理信号集合

  • 信号处理函数表

kill -9 背后就是往 PCB 里塞信号。

注:信号就是操作系统给进程发的紧急通知,

PCB 里记录了哪些通知被屏蔽、哪些已经收到但没处理、以及每种通知该怎么应对。

kill -9 这种命令,本质上就是内核往进程的 PCB 里塞一个"必须立刻死"的信号,

进程连反抗的机会都没有


9、资源使用统计

最后,PCB 还保存了进程在运行过程中产生的资源使用统计信息,例如占用的 CPU 时间、上下文切换次数以及内存使用情况。这些数据既可用于系统调度决策,也为系统监控和性能分析工具提供了基础。

  • 用户态 CPU 时间

  • 内核态 CPU 时间

  • 上下文切换次数

  • 使用的内存量

top / htop 的数据来源之一。


一个简化版 PCB 结构(示意)

复制代码
struct task_struct {
    pid_t pid;
    long state;
    int priority;

    struct mm_struct *mm;        // 内存
    struct files_struct *files;  // 打开的文件
    struct signal_struct *signal;// 信号

    struct task_struct *parent;
    struct list_head children;

    struct thread_struct thread; // CPU 上下文
};

⚠️ 实际 Linux 的 task_struct 非常大,但核心思想就是这些


好,给你一版只保留核心、好记不绕弯的总结 👇

(面试 / 实操都够用)


常用的进程查看命令

ps ------ 看"快照"

看某一时刻的进程状态

复制代码
ps aux
ps -ef
  • 看进程号(PID)

  • 看用户

  • 看 CPU / 内存

  • 看命令行

ps 看"这一刻有哪些进程"


top ------ 实时监控

动态看进程变化

复制代码
top
  • 实时 CPU / 内存

  • 排序(P 按 CPU,M 按内存)

  • 可直接 k 杀进程

top 是动态版 ps


二、按"目标"查进程(实用)

pgrep ------ 按名字找 PID

复制代码
pgrep nginx

不知道 PID,用它找


ps + grep ------ 经典组合

复制代码
ps aux | grep nginx

老派但通用


pstree ------ 看父子关系

复制代码
pstree -p

看谁 fork 了谁(后台进程 / 守护进程必用)


三、和信号 / 控制相关(你前面那章的延伸)

kill ------ 发信号

复制代码
kill PID        # 默认 SIGTERM
kill -9 PID     # SIGKILL(强制)

本质是"往 PCB 里塞信号"


jobs / fg / bg ------ shell 作业控制

复制代码
jobs
fg %1
bg %1

只管当前 shell 启动的进程

相关推荐
yanlou2332 小时前
【C++/Linux实战项目】仿muduo库实现高性能Reactor模式TCP服务器(深度解析)
linux·服务器·c++·tcp/ip·epoll
f大熊2 小时前
服务器状态监控
linux·运维·服务器·ubuntu·watchdog
等什么君!2 小时前
练习-部署nginx和部署tomcat
运维·nginx·docker
IDC02_FEIYA2 小时前
Discuz!论坛注册验证邮箱收不到邮件怎么办?邮件检测发送成功,但是收发邮箱都未看到邮件
linux·服务器·阿里云
TTBIGDATA2 小时前
【Knox】Apache Knox 2.1.0 ,开启Kerberos 后,Invalid keystore format 问题处理
运维·开源·ambari·hdp·kerberos·knox·bigtop
_OP_CHEN2 小时前
【Linux系统编程】(二十一)吃透 Linux “一切皆文件” 与缓冲区:从底层逻辑到实战封装
linux·操作系统·glibc·c/c++·缓冲区·linux文件·io库
Jtti2 小时前
怎么避免国外服务器的丢包问题?
运维·服务器
源远流长jerry2 小时前
dpdk之tcp代码案例
服务器·网络·tcp/ip
m0_737302582 小时前
腾讯云TDSQL-C+CVM,软硬协同驱动数据库性能革命
服务器