在 Linux 中,我们执行一个 Java 程序、启动一个 MySQL 服务,或者运行一个 Python 脚本,本质上都是在系统中创建并运行进程。

例如,当我们启动 Spring Boot 项目时,Linux 会为它分配进程 ID(PID),并由操作系统负责调度 CPU、分配内存以及管理进程的运行状态。

如果服务器出现 CPU 占用过高、Java 服务无响应、程序意外退出等问题,就需要使用 Linux 的进程管理命令进行排查。

1. 什么是进程?

进程(Process)是程序的一次运行实例,是操作系统进行资源分配和管理的重要单位。

例如,我们编写了一个 Java 程序:

public class Main {
    public static void main(String[] args) {
        while (true) {
            System.out.println("Hello Linux");
        }
    }
}

当我们执行这个程序时,操作系统会创建一个进程,为其分配运行所需的资源,例如内存、文件描述符等。

这里需要区分两个概念:

  • 程序(Program):存储在磁盘上的可执行代码,是一个静态概念。
  • 进程(Process):程序运行时产生的实例,是一个动态概念。

一个程序可以被多次启动,从而产生多个不同的进程。

进程的基本属性

每个进程都有自己的标识和运行信息,常见的有:

属性含义
PIDProcess ID,进程 ID,用于唯一标识当前进程
PPIDParent Process ID,父进程 ID
UID进程所属用户的 ID
GID进程所属用户组的 ID
状态表示进程当前的运行状态
CPU进程占用的 CPU 资源
内存进程使用的内存资源

其中,PID 是我们后续管理进程时最常用的信息。

2. 如何查看 Linux 中的进程?

Linux 提供了很多进程查看命令,最常用的是 ps 和 top。

2.1 ps:查看当前进程信息

ps(Process Status)用于查看进程信息。

# 查看当前终端关联的进程
ps

# 查看系统中所有进程的详细信息
ps -ef

# 查看系统中所有进程的资源使用情况
ps aux

其中,ps -ef 和 ps aux 是最常见的两种用法。

ps -ef:

ps -ef

输出示例:

UID       PID   PPID  C STIME TTY          TIME CMD
root        1      0  0 Nov05 ?        00:00:02 /sbin/init
app      1024      1  2 Nov05 ?        00:01:20 java -jar app.jar

常见字段:

字段含义
UID进程所属用户
PID当前进程 ID
PPID父进程 ID
CCPU 使用情况的调度相关指标
STIME进程启动时间
TTY关联的终端
TIME累计使用的 CPU 时间
CMD启动进程的命令

ps aux:

ps aux

常见字段:

字段含义
USER进程所属用户
PID进程 ID
%CPUCPU 使用率
%MEM内存使用率
VSZ虚拟内存大小
RSS常驻物理内存大小
STAT进程状态
START进程启动时间
COMMAND启动命令

例如,想查看 Java 进程:

ps -ef | grep java

这样就可以从所有进程中筛选出包含 java 的进程。

需要注意,grep java 本身也可能出现在结果中,可以使用:

ps -ef | grep '[j]ava'

2.2 top:实时查看进程状态

top 用于动态显示系统和进程的资源使用情况。

top

与 ps 不同,top 会持续刷新数据,方便我们观察系统运行状态。

进入 top 后,可以看到:

  • CPU 使用率。
  • 内存使用情况。
  • 系统负载(Load Average)。
  • 当前运行的进程数量。
  • 每个进程的 CPU 和内存占用。

常用快捷键:

按键作用
P按 CPU 使用率排序
M按内存使用率排序
1展开显示各个 CPU 核心的使用情况
k向指定 PID 发送信号
q退出

例如,服务器 CPU 占用突然升高,我们可以执行 top,然后按 P,查看哪些进程占用了大量 CPU。

2.3 ps 和 top 有什么区别?

对比pstop
数据展示一次性快照动态刷新
使用方式执行命令后输出结果进入交互界面
适用场景查询进程 PID、PPID 等信息实时监控 CPU、内存
数据变化不会自动刷新持续刷新

简单来说:ps 用于查询某一时刻的进程信息,top 用于实时监控进程运行情况。

3. 如何根据 PID 查找进程?

除了使用 ps,我们还可以使用 pgrep 根据进程名称查找 PID。

# 查找名称包含 java 的进程
pgrep java

# 同时显示 PID 和进程名称
pgrep -a java

如果想查看某个进程的详细信息:

ps -p 1024 -f

其中 -p 用于指定 PID。

还可以通过 pstree 查看进程之间的父子关系:

pstree -p

例如,Linux 启动过程中会形成进程之间的父子关系,通过进程树可以更直观地观察这些关系。

4. 如何终止一个进程?kill 命令

当某个程序出现异常,或者我们需要停止一个服务时,可以使用 kill 命令。

kill PID

例如:

kill 1024

这里需要注意:kill 并不意味着一定是强制杀死进程,它的本质是向指定进程发送信号(Signal)。

Linux 提供了多种信号,不同信号代表不同的控制行为。

常见信号

信号编号含义
SIGHUP1终端挂断信号,也常被部分程序用于重新加载配置
SIGINT2中断信号,通常由 Ctrl+C 产生
SIGKILL9强制终止进程,不能被捕获或忽略
SIGTERM15请求进程正常终止
SIGSTOP19(常见 Linux 架构)暂停进程,不能被捕获或忽略
SIGCONT18(常见 Linux 架构)恢复暂停的进程

其中最常用的是 SIGTERM 和 SIGKILL。

第一种:正常终止

kill -15 1024

也可以直接使用:

kill 1024

因为 kill 默认发送 SIGTERM 信号。

SIGTERM 表示请求进程正常退出,程序可以捕获这个信号,并执行清理工作,例如关闭数据库连接、释放资源、保存数据等。

第二种:强制终止

kill -9 1024

SIGKILL 会强制终止进程,进程无法捕获、忽略或处理这个信号,因此也无法执行自己的正常清理逻辑。

通常建议:

  1. 先使用 kill PID 请求程序正常退出。
  2. 等待一段时间,检查进程是否已经结束。
  3. 如果进程仍然无法退出,再考虑使用 kill -9 PID。

不要一遇到问题就直接使用 kill -9,否则可能导致资源没有正常释放,或者业务数据处于不一致状态。

5. 前台进程和后台进程

在 Linux 中,我们启动程序时,通常会遇到两种运行方式:前台运行和后台运行。

5.1 前台进程

直接执行命令:

ping www.baidu.com

程序会持续输出结果,并占用当前终端。

在程序运行期间,我们不能直接在当前终端输入其他命令,除非暂停或终止当前任务。

这种方式称为前台运行。

5.2 后台进程

如果希望程序在后台运行,可以在命令末尾添加 &:

ping www.baidu.com &

此时程序会在后台运行,终端可以继续执行其他命令。

例如:

java -jar app.jar &

这样 Spring Boot 程序就会在后台运行。

不过要注意:使用 & 只是让进程在后台运行,并不保证关闭终端后进程仍然存活。

6. jobs、bg、fg:管理当前终端的任务

Linux 提供了几个命令,用于管理当前 Shell 中的后台任务。

6.1 jobs:查看当前 Shell 的后台任务

jobs

例如:

[1]+ Running    ping www.baidu.com &
[2]- Stopped    vim test.txt

其中:

  • [1]、[2] 是任务编号(Job ID)。
  • Running 表示正在运行。
  • Stopped 表示任务已暂停。

注意:jobs 只能查看当前 Shell 管理的任务,并不是系统中所有后台进程。

6.2 Ctrl+Z:暂停当前前台任务

如果一个程序正在前台运行,可以按:

Ctrl + Z

这会向前台进程组发送 SIGTSTP 信号,通常使当前任务暂停。

6.3 bg:让暂停的任务在后台继续运行

例如,先使用 Ctrl+Z 暂停任务,然后执行:

bg

就可以让最近暂停的任务在后台继续运行。

也可以指定任务编号:

bg %1

6.4 fg:将后台任务切换到前台

如果想让后台任务重新回到前台,可以使用:

fg

或者:

fg %1

简单总结:

命令作用
jobs查看当前 Shell 的任务
Ctrl+Z暂停当前前台任务
bg让暂停任务在后台继续运行
fg将后台任务切换到前台

7. 如何让程序在终端关闭后继续运行?

前面我们介绍了使用 & 将程序放到后台,但它仍然可能受到终端退出的影响。

例如:

java -jar app.jar &

如果关闭终端,Shell 退出时可能会向相关进程发送 SIGHUP,导致程序终止。

如果希望程序在终端关闭后继续运行,可以使用 nohup。

7.1 nohup:忽略终端挂断信号

nohup java -jar app.jar &

其中:

  • nohup:让命令忽略 SIGHUP 信号。
  • &:让命令在后台运行。

默认情况下,程序的输出会写入当前目录下的 nohup.out 文件(前提是标准输出未被重定向)。

更推荐在实际部署时指定日志文件:

nohup java -jar app.jar > app.log 2>&1 &

这里有几个重要知识点:

符号作用
>将标准输出重定向到文件
2>将标准错误重定向到文件
2>&1将标准错误重定向到标准输出当前指向的位置
&让命令在后台运行

因此:

nohup java -jar app.jar > app.log 2>&1 &

表示:

  1. 使用 nohup 忽略终端挂断信号。
  2. 将标准输出写入 app.log。
  3. 将标准错误也写入 app.log。
  4. 将程序放到后台运行。

7.2 如何查看后台 Java 服务?

# 查看 Java 进程
ps -ef | grep '[j]ava'

# 实时查看日志
tail -f app.log

# 停止程序
kill PID

不过,nohup 适合简单的后台运行场景。生产环境中,更推荐使用 systemd 管理服务,这样可以统一管理服务的启动、停止、重启、开机自启和日志。

8. 进程和线程有什么区别?

这部分与我们之前学习的 JVM 和 Java 多线程知识有关。

进程是资源分配和管理的重要单位,线程是 CPU 调度的基本单位。

一个进程可以包含多个线程,这些线程共享进程的大部分资源,例如进程地址空间和打开的文件等。

例如,一个 Spring Boot 应用启动后,Linux 会创建对应的 Java 进程,而 JVM 内部还会创建多个线程,用于处理请求、执行任务、进行垃圾回收等。

可以通过下面的命令查看某个进程中的线程:

# 查看指定进程中的线程
ps -T -p 1024

也可以使用:

top -H -p 1024

其中:

  • -T:显示指定进程中的线程。
  • -H:在 top 中显示线程。

注意:Linux 内核中线程也有自己的任务标识(TID),但它们属于同一个线程组,共享进程的大部分资源。

9. 实战:排查 Spring Boot 服务进程

假设我们在 Linux 服务器上部署了一个 Spring Boot 项目,启动命令如下:

nohup java -jar app.jar > app.log 2>&1 &

现在我们需要检查服务是否正常运行。

第一步:查找 Java 进程

ps -ef | grep '[j]ava'

第二步:查看 CPU 和内存占用

top

按 P 可以按照 CPU 使用率排序,按 M 可以按照内存使用率排序。

第三步:查看程序日志

tail -f app.log

第四步:查看进程中的线程

假设 Java 进程 PID 为 1024:

ps -T -p 1024

第五步:停止服务

kill 1024

如果程序没有正常退出,再根据实际情况考虑:

kill -9 1024

通过这些命令,我们就可以完成 Java 服务的基本进程排查和管理。

10. 总结

Linux 中常见的进程管理知识:

命令作用
ps查看进程信息
top实时监控系统和进程
pgrep根据进程名称查找 PID
pstree查看进程树
kill向进程发送信号
jobs查看当前 Shell 的后台任务
bg让暂停任务在后台运行
fg将后台任务切换到前台
nohup忽略 SIGHUP 信号运行程序
&让命令在后台运行

对于后端开发来说,进程管理是 Linux 非常重要的一部分。无论是部署 Java 服务、排查 CPU 占用过高,还是处理程序异常退出,都离不开这些命令。

Logo

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

更多推荐