欢迎光临
我们一直在努力

linux中的进程

进程是什么

1. 进程

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

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

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

其“动态性”体现在:

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

  • 有不断变化的执行状态

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

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


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

结论性表述

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

进程 = 内核中的进程描述数据结构     + 该进程所运行的程序代码与数据

这一定义同时涵盖了:

  • 操作系统如何“认识”进程

  • 进程实际“执行”的内容


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

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

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

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

  • 进程的唯一标识(PID)

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

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

  • 内存使用情况

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

  • 与其他进程的父子关系

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

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

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


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

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

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

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

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


二者的分工与关系

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

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

二者缺一不可:

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

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

因此:

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


推论

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

  • 同一程序可以对应多个进程

    • 代码可以共享

    • 内部数据结构彼此独立

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

    • 操作系统“看见”的是进程

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

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

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


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


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

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

    赞(0)
    未经允许不得转载:171主机测评 » linux中的进程
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址