当前位置:首页>Linux>Linux 进程基础:程序、进程、PCB、地址空间和进程状态

Linux 进程基础:程序、进程、PCB、地址空间和进程状态

  • 2026-09-28 08:49:37
Linux 进程基础:程序、进程、PCB、地址空间和进程状态
点击上方
蓝字
关注我,设置星标不迷路哈

不知道你有没有过这样的经历:

刚接触 Linux 的新手,跟着教程敲了个 ps aux,出来一屏幕密密麻麻的内容。教程说“这就是系统正在跑的进程”,你点点头假装听懂了,但转头还是想问:进程到底是个啥?它和程序有啥不一样?为什么有的进程会占满 CPU?为什么杀进程有时候还杀不掉?

如果你也有这些疑问,那这篇文章正好帮你把 Linux 进程讲明白。我们先从最基础的概念讲起,一步步拆解进程的本质。看完这一篇,你至少能搞懂三个问题:

  • 程序和进程到底差在哪?
  • Linux 内核是怎么管理进程的?
  • ps、top 里看到的 R、S、D、Z、T 到底是什么意思?

第一个问题:程序和进程,到底差在哪?

很多新手刚入门,最容易搞混的就是程序和进程,我们先把这个关系理清楚。

举个生活里的例子:你下载了一个微信的安装包,安装到电脑里,这个躺在你硬盘里的微信,就是程序。

程序本质上是一堆存放在磁盘上的二进制代码、配置文件和资源文件。它是静态的,占的是磁盘空间,不跑起来的时候,它不会主动做任何事情。

什么时候它变成进程呢?

当你双击打开微信,系统把它加载到内存里,给它分配 CPU 时间、内存、文件描述符等资源,让它开始处理消息、网络连接和界面操作时,这个正在运行的微信,就是一个进程。

这么说是不是就好理解了?总结一下两者的区别:

  • 程序是静态的,存在磁盘上,是一份可以被执行的文件或文件集合。
  • 进程是动态的,是程序运行起来之后在系统里的执行实例。
  • 程序不运行时不会占用 CPU;进程运行时会占用 CPU、内存、文件、网络连接等系统资源。
  • 进程结束后就不存在了,但程序文件通常还在磁盘上。

一个程序也可以对应多个进程。

比如你多次运行同一个脚本,或者同一个 Web 服务启动了多个 worker 进程,这些进程运行的可能是同一份程序代码,但每个进程都有自己的 PID、运行状态和资源信息。现代浏览器的多个标签页、渲染进程、插件进程也经常采用多进程模型,但具体是否“一页一个进程”要看浏览器实现和配置,不能简单地一概而论。


第二个问题:进程在系统里到底是什么样的存在?

理解了基本概念,我们再往深挖一点:Linux 系统里,进程本质上是什么东西?

Linux 是一个多任务操作系统。你可以一边跑 Web 服务,一边跑数据库,还能远程登录服务器查日志,看起来这些任务都在“同时运行”。

但对单个 CPU 逻辑核来说,同一时刻通常只能执行一个任务流。Linux 之所以能让我们感觉很多程序在同时运行,是因为内核会让不同进程在 CPU 上快速切换:这个进程运行一小会儿,切到下一个进程,再切到另一个进程。切换速度足够快,我们就感觉它们在同时工作。

为了管理这么多进程,Linux 内核必须给每个进程做一套“信息登记”。否则它就不知道哪个进程跑到哪了、占了哪些资源、该不该继续调度。

所以在 Linux 里,理解进程时最关键的两个概念是:

  1. 进程控制块 / 进程描述符:内核用来记录和管理进程信息的数据结构。
  2. 进程地址空间:进程运行时看到的虚拟内存空间。

先说说进程控制块,它到底存了啥?

很多人没听过 PCB。PCB 是操作系统教材里常见的说法,意思是 Process Control Block,进程控制块。

在 Linux 内核里,和 PCB 对应的核心数据结构通常叫 task_struct,也可以理解为 Linux 的“进程描述符”。内核会用它记录每个进程的关键信息,我们挑几个最重要的说:

  • 进程 ID(PID):每个进程启动时,系统都会给它分配一个编号。同一时刻,PID 可以用来定位一个具体进程。需要注意的是,进程退出后 PID 可能会被系统重新分配给新的进程,所以 PID 不是永久身份证,而是“当前有效的编号”。
  • 父进程 ID(PPID):记录是谁创建了这个进程。通过 PPID,我们可以看到进程之间的父子关系。
  • 进程状态:进程不是一直都在跑,它可能正在运行,也可能在等待 I/O、等待网络、暂停,甚至已经退出但还没被父进程回收。
  • 调度信息:包括优先级、调度策略、时间片、运行队列等信息。它们会影响进程什么时候能拿到 CPU。
  • 资源信息:进程使用的内存、打开的文件、信号处理方式、命名空间、权限信息等,会由 task_struct 直接记录或关联到其他内核结构。
  • 上下文信息:进程切换时非常关键。当一个进程让出 CPU 时,内核要保存它当前运行到哪里、寄存器值是什么、栈在哪里。下次再调度它时,内核把这些信息恢复回来,它就能接着往下跑。

简单说,没有这些进程描述信息,内核就没法管理进程。你可以把 task_struct 理解成进程在内核里的“档案袋”。

再说说进程地址空间,每个进程都有自己的“独立小房间”

很多新手会问:

为什么一个进程崩溃了,不会把别的进程也带崩?为什么每个进程都好像拥有一大片连续内存?

这就是虚拟地址空间在起作用。

每个进程都有自己独立的虚拟地址空间。简单说,Linux 给每个进程画了一个“独立小房间”:进程看到的是自己的虚拟地址,而不是直接操作真实的物理内存。

以 32 位系统为例,一个进程理论上可以看到 4GB 左右的虚拟地址范围,但实际可供用户程序使用的范围会受到内核空间和用户空间划分的影响,例如常见的 3G/1G 划分。64 位系统的虚拟地址空间更大,但也不是说物理内存真的有那么大。

真正使用内存时,内核会通过页表把虚拟地址映射到物理内存。没真正用到的地址,不一定会马上分配物理内存。

这么做有什么好处呢?

  1. 隔离性更好每个进程默认只能访问自己的地址空间,不能随便读写别的进程的内存。所以一个普通进程崩溃,通常不会直接把其他进程也带崩。

  2. 内存管理更方便程序不需要关心物理内存到底在哪。哪怕物理内存上是零散的,进程看到的虚拟地址也可以是连续的,开发和管理都更方便。

  3. 支持更灵活的内存使用操作系统可以结合按需分配、内存映射、写时复制、交换空间等机制,让内存使用更灵活。但要注意,虚拟地址空间很大,不代表系统一定能真正提供那么多可用内存。

所以你看,进程并不是“代码跑起来”这么简单。它是内核管理的一个执行实体,有编号(PID)、有档案(进程描述符)、有自己的地址空间,也有自己的资源和状态。


第三个问题:进程都有哪些状态?一看就懂

我们经常在 ps 或者 top 命令里看到进程状态,比如 S、R、Z 这些字母。很多人不知道它们是什么意思,其实它们表示的是进程当前处于什么阶段。

Linux 里的进程状态不止下面这些,但对新手来说,先掌握这几个最常见的就够用了。

状态名称
缩写
通俗解释
生活类比
运行态 / 就绪态
R
正在 CPU 上运行,或者已经准备好,就等 CPU 调度
你已经坐在电脑前,打开文档,随时可以开始写代码
可中断睡眠
S
进程在等待某个事件,比如等待用户输入、等待网络数据,期间可以被信号唤醒
你在工位等外卖,外卖没来前可以刷手机,有人喊你也能马上回应
不可中断睡眠
D
进程在等待某些内核态事件,常见是磁盘、NFS、块设备等 I/O,期间通常不能被信号立即打断
你正在做一件不能中途停下的操作,必须等这一步结束才能处理别的事情
僵尸态
Z
子进程已经退出,但父进程还没有读取它的退出状态
你已经离职,但系统里还保留着一条待确认记录
停止态
T
进程被暂停了,收到继续信号后可以恢复运行
你手上的活被临时叫停,等通知后再继续

我们再说说平时最容易遇到的几个特殊状态。

1. 为什么会有 D 状态(不可中断睡眠)?什么时候会出现?

D 状态一般出现在进程等待某些不可中断的内核态操作时,最常见的是磁盘 I/O、NFS 网络存储、块设备、驱动层调用等。

比如一个进程要从磁盘读取大文件,或者访问的 NFS 存储突然无响应,进程可能就会卡在 D 状态。这个状态下,信号不会马上生效,所以你就算执行 kill -9 PID,进程也不一定立刻消失。

准确地说,不是 kill -9 没发出去,而是进程还卡在不可中断等待里,内核要等它从这个等待中返回,才有机会处理相关退出逻辑。

如果系统里出现大量 D 状态进程,大概率要优先排查 I/O 问题,比如:

  • 磁盘是否故障;
  • 存储链路是否异常;
  • NFS、iSCSI、Ceph 等网络存储是否不可达;
  • 文件系统或块设备是否卡住。

2. 僵尸进程是怎么来的?为什么会产生僵尸进程?

进程退出后,父进程需要通过 wait() 或 waitpid() 读取子进程的退出状态,并让内核清理最后的进程记录。

如果子进程已经退出,但父进程一直没有回收它,那么这个子进程就会变成僵尸进程。

那僵尸进程有害吗?

少量僵尸进程一般影响不大,因为它已经不运行了,也不再占用真正的 CPU 和大量内存。它主要占用的是 PID 和一小部分进程表记录。

但如果系统里大量产生僵尸进程,就可能导致 PID 被耗尽,新的进程无法创建,这就会变成严重问题。

怎么处理僵尸进程?

普通的 kill 命令对僵尸进程没用,因为它已经退出了。正确思路是:

  1. 找到僵尸进程的父进程;
  2. 判断父进程为什么没有回收子进程;
  3. 优先修复或重启父进程;
  4. 如果父进程退出,僵尸进程通常会被 PID 1 或子进程收割器接管并回收。

生产环境里不要一上来就随便杀父进程,因为父进程可能是重要服务。应该先确认影响范围,再决定是重启服务、修复程序,还是在维护窗口处理。

3. 孤儿进程又是什么?

孤儿进程和僵尸进程刚好相反。

如果父进程先退出了,子进程还在运行,这个子进程就变成了孤儿进程。

孤儿进程不会没人管。Linux 会让它被 PID 1,或者某个被设置为子进程收割器的进程接管。之后等它退出时,会由新的父进程负责回收。

所以孤儿进程本身并不一定是问题。它是否需要处理,主要看它是不是你期望继续运行的服务或任务。


到这里,我们已经把 Linux 进程的基础部分理清楚了:程序是静态文件,进程是运行中的执行实例;内核通过进程描述符管理进程;每个进程有自己的虚拟地址空间;不同进程状态代表了它当前的运行阶段。

下一篇我们继续往下看:一个进程到底是怎么启动的?日常排查时,ps、top、kill、nohup 又应该怎么用?

END

yum 安装包为什么总和镜像源有关?换源、离线安装和生产环境避坑(下)

yum 安装包为什么总和镜像源有关?先搞懂 Linux 软件仓库这套逻辑(上)

为什么 Linux 的目录这么奇怪?终于有人讲明白了

为什么 Linux 要用“挂载”?而不是直接显示 C 盘、D 盘?

最新文章

随机文章