进程在 Linux 中可以说是一个非常重要的概念,上到系统内核,下到一个很小的脚本,甚至是 ls 命令,都是进程的缩影。可能很多人对进程的概念还不是很清楚,以及具体怎么做,怎么管理等等。今天我们就分两个部分,详细的讲一下 Linux 的进程概念。

什么是进程

教科书级别的解释是:进程是程序的执行实例,是系统资源分配和独立运行的基本单位。

不要管那么多。说得不恰当一些,进程就是某个人现在正在做的事情,严格的说是任务。只不过放到系统层面而言,我们把它抽象化了。

比如说,出去买菜,这就是一个进程。买菜回来做饭,这也是一个进程。

进程的几个状态

  1. 运行:很简单,一个进程正在运行或者在运行队列中。比如说:正在买菜。

  2. 睡眠:一个进程正在等待某个事件完成(例如等待 I/O,有点像数据库里面的死锁),但可以被终止,又叫中断睡眠(浅度睡眠)。比如说:买菜的路上接到家里人的电话。

  3. 磁盘休眠:又叫不可中断睡眠(深度睡眠),说明一个进程必须等待某个事件完成才能继续,并且此时不可以被终止。比如说:买菜在等摊主找零,必须等找完才能离开。

  4. 停止:一个进程被暂停,但稍后可以被恢复继续。比如说:买菜的路上先去买了一杯棒打鲜橙或者杨枝甘露。

  5. 僵尸:大多出现在子进程内,指的是子进程结束(停止)后,父进程因某些原因未能读取子进程信息的状态。比如说:A 委托 B 去买东西,B 买完之后通知 A,A 恰好在忙,没来得及理会。

  6. 死亡:持续时间极短,只是一个返回状态,用于告知系统一个进程已经完成使命,随后系统会立即释放该进程占有的资源。比如说:B 买完了东西回来,A 拿到东西后让 B 去忙自己的事情。

  7. 孤儿:和僵尸状态相对,指的是子进程仍然在运行时,父进程因某些原因突然结束或异常终止的状态。此时子进程会被 systemd 进程(系统启动时最先运行的进程,编号为 1,直至系统关机。某些老的系统内是 init)“收养”,直至结束。比如说:A 和 B 买菜时,A 让 B 去某个摊位买梭子蟹,B 在买鱼的时候,A 突然接到电话说要立即去开会,就立即赶到公司,B 回来时,找不到 A.

管理进程

Linux 系统提供了多个命令方便我们检查和管理进程。

top, htop 和 btop

这三个程序都能直观地(动态)显示当前系统的资源状况和进程。

其中 top 是系统自带,而 htopbtop(别称 btop++)需要自己安装。当然,在某些系统内,htop 也是预装的。

btop 为例,安装的命令如下:

# Debian/Ubuntu
sudo apt install btop

# Fedora
sudo dnf install btop
# For RHEL/CentOS/AlmaLinux 8+/RockyLinux:
# You need to install "epel-release" first.

# Arch Linux
sudo pacman -S btop

# openSUSE
sudo zypper in btop

# A manual installation for other distributions is mandatory

直接输入对应的程序名称(例如 btop)即可查看当前系统进程状态:

虽然上面用的是更加先进的 btop,但我们还是有必要科普一下 top 每一行的含义。

在下面一个 top 界面中:

第一行的参数如下表:

名称

含义

top

程序名

23:22:26

当前时间

up 14:58

从上一次开机算起,服务器已经不间断运行14小时58分钟

某些还会带有“XX days”,表示已经运行 XX 日

2 users

当前系统登录的用户数目为 2

load average: 0.06, 0.07, 0.08

表示 CPU 在 1 分钟、5 分钟和 10 分钟内的平均负载分别为 0.06, 0.07 和 0.08

第二行的参数如下表:

名称

含义

Tasks

系统内的任务/进程

292 total

当前系统内的任务/进程一共有 292 个

1 running

有 1 个任务/进程正在运行

291 sleeping

有 291 个正在睡眠的任务/进程

0 stopped

没有任务/进程已经停止

0 zombie

没有僵尸进程

第三行的参数如下表:

名称

含义

%Cpu(s)

CPU 的使用占比

2.3 us

2.3% 为未改变优先级的用户进程(user)

2.3 sy

2.3% 为系统自身或内核空间(system)

0.0 ni

0% 为改变优先级的用户进程(在 Linux 内称为 nice)

95.3 id

95.3% 为空闲 CPU(idle)

0.0 wa

0% 为等待 I/O 操作时占用的(wait)

0.0 hi

0% 为硬件中断占用的(hardware interrupt)

0.0 si

0% 为软件中断占用的(software interrupt)

0.0 st

0% 为虚拟机“偷取”的(stolen)

第四行的参数如下表:

名称

含义

MiB Mem:

物理内存

3621.2 total

当前系统内存总量为 3621.2 MiB

411.2 free

当前系统空闲内存为 411.2 MiB

2295.3 used

当前系统已经使用的内存为 2295.3 MiB

1218.0 buff/cache

当前系统已缓存的内存为 1218.0 MiB

第五行的参数如下表:

名称

含义

MiB Swap

交换空间虚拟内存)

3280.0 total

当前系统交换空间的总量为 3280.0 MiB

3280.0 free

当前系统空闲交换空间为 3280.0 MiB

0.0 used

当前系统尚未使用交换空间

1352.9 avail Mem

当前系统可用的交换空间为 1352.9 MiB

第六行(空行不算)的参数如下表:

名称

含义

PID

进程的 ID

USER

创建进程的用户

PR

进程的优先级(priority)

NI

进程的优先级(nice),越低表示优先级越高,可以小于 0

VIRT

进程使用的虚拟内存(交换空间)总量

RES

进程使用的物理内存总量

SHR

进程使用的共享(shared)内存总量

S

进程的状态(status)

R = 运行,S = 睡眠, D = 不可中断睡眠,I = 空闲,T = 由任务控制信号停止,t = 在跟踪过程中被调试器停止,Z = 僵尸

%CPU

进程占用 CPU 的比率

%MEM

进程占用内存的比率

TIME+

从进程启动开始到目前为止所占用的 CPU 时间

COMMAND

进程的名称

下面就是一些进程的详细信息,这里就不多讲了。

一些使用技巧如下:

top -d 1
# refresh by 1 second

top -d 1 -p 10126
# look at information of process 10126

top -d 1 -p 10126,1
# look at processes 10126 and 1

查看一个进程

Linux 内有一个命令 ps(不是 P 图的那个!它的全称是 process management,进程状态)可以静态查看进程。这样获取到的进程信息是固定的某一瞬间,有点像抓拍到的照片。

我们先运行这个命令:

ps aux

结果如下:

输出的内容非常多,这里只是截取的一部分。

我们先来看看字段名称:

  • USER:指的是创建进程的用户,也就是哪个用户(你还记得几种?)创建了这个进程。

  • PID:指的是进程的 ID. 系统会从 1 开始(对应的是 systemd)将每一个进程编号。这个字段非常重要,因为后面当我们需要杀死进程时就需要进程的 PID.

  • %CPU:指的是该进程占用 CPU 的比率

  • %MEM:指的是该进程占用内存的比率

  • VSZ:指的是该进程占用虚拟内存(或者交换空间)的量

  • RSS:指的是该进程占用实际内存的量(不是 RSS 订阅!)。

  • TTY:指的是该进程运行的终端(后面会讲)。“?”表示该进程由系统启动。

  • STAT:指的是进程的状态。主要分为 R, S, T, Z, X(运行,睡眠,停止,僵尸,死亡)五种。

  • START:指的是该进程启动的时间

  • TIME:指的是该进程占用 CPU 的总时间

  • COMMAND:指的是该进程的名称

一个比较常用的方法是,将该命令和管道(后面会讲)结合使用。例如,要查找名称为 sleep 114 的进程(假设没有其他进程创建了这个进程):

ps aux | grep sleep

在使用 ps 命令时,我们也可以指定参数,以期获得不同排序指标下的进程状态。

例如,按照 CPU 占用率排序(默认是升序,如果要降序在 %cpu 前加“-”;有没有“=”连接 --sort%cpu 其实都可以):

另一个常用的命令是 ps -ef ,它能以完整格式(从 Full Format 直译的,我也不清楚是什么)列出所有的进程:

这个命令还会列出父进程的 PID(0 就是不存在),这样可以方便我们查看进程可能的依赖关系。

ps 命令也支持自定义字段:

自定义字段名必须参照原格式,中间用逗号分隔,并且不允许有空格。

杀死进程

kill 命令可以用来杀死(强制终止)某个进程,也可以向某个进程发送其他的一些信号。

下面是这个命令支持的一部分信号(可以通过 kill -l 查阅):

编号

信号名称

含义

1

SIGHUP

重新加载配置

2

SIGINT

键盘中断(Ctrl + C)

3

SIGQUIT

键盘退出(Ctrl + \),类似于 SIGINT

9

SIGKILL

强制终止(最常用)

15

SIGTERM

正常结束某个进程(默认)

18

SIGCONT

继续结束的进程

19

SIGSTOP

暂停该进程

20

SIGSTP

键盘暂停(Ctrl + Z)

相应的,该命令的格式如下:

kill -[SignalNo] [SID]

例如:

kill -9 14871
# KILL the process with PID of 14871

kill -15 14888
# Terminate the process with PID of 14888

进程优先级

Linux 系统内存在 2 种优先级,分别是 PR 值和 nice 值。下面我们来重点说说 nice 值。

进程的优先级(nice)是一个位于区间 [-20, 20)整数数值,其中在区间映射的 x 轴上,数值越靠左(越靠近 -20),该进程的优先级越高,反之越低。这和数学上的负数意义还是不一样的!

至于另一种 PR 值,则是将 nice 值映射到更大的优先级队列,即将 -20 映射到 0,+19 映射为 39. PR 值也允许负值,且下不兜底。

那么,为什么必须要有优先级呢?像人一样,在每一个时间点上每一个 CPU 只能处理一个进程,因此 CPU 必须通过明白各个进程的轻重缓急,优先决定哪些进程要先处理,再结合时间片技术,进而同时运行多个程序。

查阅进程的优先级

下面的命令可以查看进程的 nice 级别:

ps aux --sort=-nice | head -5

结果如下:

更改优先级

我们可以通过 nicerenice 命令调整进程的优先级。

默认情况下,一个进程启动时,会继承父进程的优先级。要手动指定之,我们应该在该进程的前面使用 nice 命令:

nice -n -5 sleep 3600 &

这个命令会创建一个 nice 值为 -5,名为 sleep 3600 的进程。后面的 & 表示创建该进程后,任其自然(不影响后续命令的执行,不会表现出等待 I/O),且仅返回其 PID.

如果在上面不小心输错了优先级怎么办?此时,renice 就作为“后悔药”来解决这一问题了。它的基本用法如下:

renice [newNiceValue] [PID] 

例如:

结果是这样的:

更高级的用法可以参阅这里:

https://www.runoob.com/linux/linux-comm-renice.html

总结

进程是计算机中正在运行的程序的实例,它具有创建、执行和结束的生命周期,并包含代码、数据和系统资源;进程在其生命周期中会经历多个状态,主要包括就绪、运行、阻塞和终止等,这些状态反映了进程在CPU和内存中的活动情况。在管理进程方面,用户可以使用命令行工具如 top、htop 和 btop 来实时监控系统进程和资源使用情况,通过命令如 ps 查看特定进程的详细信息,并在需要时使用 killpkill 命令终止失控进程;此外,进程优先级(如 nice 值)允许用户调整进程的CPU调度顺序,可以通过 topps 命令查看优先级,并使用 renicenice 命令动态更改它以优化系统性能。总之,理解进程及其管理对于维护系统稳定和高效运行至关重要。

C/C++ 新人开发者,主攻 UE5,业余 Linux 运维