第十章:进程与服务管理:化身系统指挥官
第十章:进程与服务管理:化身系统指挥官
Prorise第十章:进程与服务管理:化身系统指挥官
摘要: 在前面的章节中,我们已经搭建并优化了开发环境。然而,一个真正的工匠不仅要会使用工具,更要能洞察工具箱内部的运作状态。本章将带你深入 Linux 的“引擎室”——进程与服务管理。我们将学习如何精准地定位、诊断和管理系统中的每一个活动进程,掌握与它们“沟通”的正确方式。更重要的是,我们将攻克“终端关闭,任务中断”、“端口被占用”等一系列开发者日常的最高频痛点,并最终学会使用现代化的 systemd
,像一个真正的运维专家那样,管理需要长期稳定运行的后台服务。
本章学习地图:
在本章中,我们将循序渐进,建立对系统活动全局掌控的能力:
- 首先,我们将掌握进程的 侦查与诊断,学会如何从系统的万千活动中,快速定位到我们关心的目标。
- 接着,我们将学习 信号的艺术,理解如何与进程进行“优雅”或“强制”的沟通,确保服务的平稳启停。
- 然后,我们将攻克开发者最常见的痛点:任务的持久化,使用
nohup
和终极武器tmux
构建一个永不掉线的开发会话。 - 之后,我们将化身 网络侦探,解决“端口被占用”这一高频难题。
- 最后,我们将接触现代 Linux 系统的基石——systemd 服务管理,学习像运维专家一样管理和部署后台应用。
10.1. 侦查与诊断:透视系统脉搏
痛点背景:
想象这个场景:你的应用突然响应迟缓,系统风扇狂转,CPU 负载升高;或者,你刚刚在后台启动了一个数据处理脚本,现在想确认它是否还在正常运行。在这些时刻,我们首先需要一套精准的侦查工具来定位问题进程,看穿系统当前的脉搏。
静态快照: 经典组合拳 ps aux | grep
ps
(process status) 是查看进程状态的基石命令。它会给你拍下一张当前系统进程的“静态快照”。单独使用 ps
信息有限,我们通常会附带 aux
选项:
a
: 显示所有用户的进程。u
: 以用户为中心,显示更详细的信息。x
: 同时显示没有控制终端的进程(比如系统后台服务)。
动手操作:初探进程列表
直接运行 ps aux
会输出海量的信息,通常我们会用管道符 |
将结果传递给 grep
来进行过滤。例如,我们想查找 WSL 自带的 SSH 服务进程 sshd
。
1 | ps aux | grep sshd |
1
2
root 10 0.0 0.0 8892 4884 ? Ss Sep17 0:00 /usr/sbin/sshd -D
prorise 1234 0.0 0.0 6836 2248 pts/0 S+ 22:05 0:00 grep --color=auto sshd
这个输出告诉我们很多信息:
- 第一行就是我们想找的
/usr/sbin/sshd
服务进程,它的所有者是root
,进程 ID (PID) 是10
。PID 是进程的唯一身份证号,后续我们与进程交互都将依赖它。 - 第二行是
grep
命令本身。因为我们执行的grep sshd
这个动作本身也是一个进程,所以它也被自己找到了。这是一个经典的小干扰。
实时仪表盘: htop
(top 的终极替代品)
ps
提供的都是“静态快照”,如果你想实时监控系统状态,就需要一个动态的“仪表盘”。传统的工具是 top
,但它的界面简陋,操作也不够直观。在 2025 年,我们毫无疑问应该选择它的终极替代品:htop .
htop
提供了一个彩色的、可交互的界面,让你能用更直观的方式排序、过滤、甚至直接管理进程。
动手操作:安装并体验 htop
第一步:安装 htop
htop
通常不是默认安装的,我们需要手动安装一下。
1 | sudo apt update && sudo apt install htop -y |
第二步:启动 htop
直接在终端输入命令即可。
1 | htop |
你的整个终端窗口会被 htop
的交互界面接管。
htop
核心功能导览
你不需要记住所有功能,但以下几个是开发者排查问题的“利器”:
- 实时监控: 顶部的 CPU(每个核心一个进度条)、Mem(内存)、Swp(交换空间)使用率让你对系统负载一目了然。
- 排序: 用鼠标直接点击
CPU%
或MEM%
这些列标题,或者按 F6,可以快速找到最耗费资源的应用。 - 过滤 (F4): 按下
F4
,在底部输入sshd
,进程列表会立即只显示与sshd
相关的进程。 - 树状视图 (F5): 按下
F5
,进程会以树状结构展示,清晰地显示出谁是谁的父进程,对于理解复杂应用的进程关系非常有帮助。 - 发送信号/杀死进程 (F9): 选中一个进程,按下
F9
,会弹出一个信号菜单(我们下一节会详细讲),你可以选择发送SIGTERM
(15) 来优雅地关闭它,或者在它卡死时发送SIGKILL
(9) 来强制终结。
当你完成侦查后,按下 q 或 F10 即可退出 htop
。
现在,我们已经掌握了从静态到动态、从粗略到精确的各种进程侦查手段。接下来,我们将学习如何使用这些侦查结果,与进程进行“沟通”。
10.2. 信号的艺术:与进程的优雅沟通
痛点背景: 想象一下,一个正在向数据库写入重要数据的应用程序,或者一个正在处理用户上传文件的大型 Web 服务。如果此时我们粗暴地“拔掉电源”,极有可能导致数据文件损坏、数据库记录不一致或产生难以清理的临时文件。我们需要一种机制,能够通知进程:“请准备一下,你即将关闭”,让它有机会完成收尾工作,这就是 Linux 信号机制的价值所在。
10.2.1. 核心信号理论
信号 (Signal) 是 Linux/Unix 系统中进程间通信的一种基本方式。它是一个异步的通知,由内核发送给某个进程,以告知其发生了某个事件。进程可以捕获并处理大部分信号,执行自定义的清理逻辑。对于开发者和系统管理员而言,必须掌握以下三个最关键的信号:
SIGINT 2
:Interrupt Signal
,中断信号。它等同于我们在终端中按下 Ctrl+C。它通知前台进程“用户希望中断你”,大部分程序会捕获这个信号并立即终止。SIGTERM 15
:Terminate Signal
,终止信号。这是最常用、最“优雅”的关闭信号,也是kill
命令的 默认信号。它像一封正式的解雇通知,告诉进程“请你终止”,并给予进程时间来执行清理操作,如保存进度、关闭文件句柄、释放资源等。这是我们应该首选的进程关闭方式。SIGKILL 9
:Kill Signal
,杀死信号。这是一个“终极”信号,它由内核直接执行,会立即、强制地终止进程。进程 无法捕获、阻塞或忽略 此信号。这相当于直接切断电源,进程没有任何机会进行清理。
🤔 思考一下
为什么 SIGKILL
的编号是 9
而 SIGTERM
是 15
?
10.2.2. 信号发送工具
掌握了理论后,我们来看看发送这些信号的具体命令。
kill
这是最基础的命令,通过 进程 ID (PID) 来精确地向单个进程发送信号。
语法: kill -<信号编号或名称> <PID>
实战场景: 假设我们在上一节中找到的 npm run dev
进程(PID 为 8765)无响应了。
1 | # 首先,尝试发送默认的 SIGTERM 信号,给它一个优雅退出的机会 |
1
[1]+ Killed npm run dev
pkill
& killall
当处理按名称分类的一组进程,或者不想先用 pgrep
查找 PID 时,pkill
和 killall
就派上了用场。
pkill <name>
: 根据进程名 部分匹配 来发送信号。killall <process_name>
: 根据进程名 精确匹配 来发送信号。
pkill
非常适合处理由主进程衍生出多个子进程的场景。
实战场景: 假设一个 Node.js 应用启动了多个工作进程。
1 | # 优雅地关闭所有包含 "node" 关键字的进程 |
killall
在你确定要杀死所有同名的、独立的进程时非常有用。
实战场景: 比如系统中意外地运行了多个 my_script.sh
实例。
1 | # 强制杀死所有名为 my_script.sh 的进程 |
使用警告:kill -9
是最后的防线,而非首选方案!
滥用 kill -9
是一个坏习惯。它应该只在进程完全僵死、对 SIGTERM
毫无响应的情况下使用。在生产环境中,优先使用 systemctl stop
或 kill
命令,给应用一个安全关闭的机会,是专业运维的基本素养。
分类 | 关键项 | 核心描述 |
---|---|---|
核心信号 | SIGTERM (15) | (首选) 优雅终止信号,给予进程清理时间。kill 命令默认发送此信号。 |
SIGINT (2) | 中断信号,等同于 Ctrl+C 。 | |
SIGKILL (9) | (慎用) 强制杀死信号,由内核执行,进程无法拒绝。 | |
发送工具 | kill <PID> | (最常用) 通过 PID 精确控制单个进程。 |
pkill <name> | 通过进程名 部分匹配 来批量控制进程。 | |
killall <name> | 通过进程名 精确匹配 来批量控制进程。 |
10.3. 任务持久化(一):简单的后台执行与 nohup
痛点背景: 您是否经历过这样的场景:通过 SSH 连接到服务器,启动了一个需要运行数小时的数据迁移脚本,然后因为网络波动连接中断,导致整个任务前功尽弃?或者,您在本地 WSL 中启动了一个 Web 应用的编译打包任务,却因为不小心关闭了 Windows Terminal 窗口而被迫重来?这就是任务持久化的必要性所在。
10.3.1. 临时后台任务:&
, jobs
, fg
, bg
最简单的让任务在后台运行的方式,是在命令末尾加上一个 &
符号。
1 | # 启动一个后台睡眠任务,它会持续 300 秒 |
1
[1] 12345
这里的 [1]
是 作业号 (Job ID),而 12345
是我们熟悉的 进程号 (PID)。这个作业现在就在当前 Shell 的后台运行,不会阻塞我们继续输入其他命令。我们可以使用 jobs
命令来查看当前 Shell 会话中的所有后台作业。
1 | # 查看当前会话的后台作业 |
1
[1]+ 12345 Running sleep 300 &
我们可以通过 fg
(foreground) 和 bg
(background) 命令,结合作业号(需要加 %
前缀),来灵活地调度它们:
fg %1
: 将作业号为 1 的任务调回 前台 运行。此时终端会被该任务占据,按下Ctrl+Z
可以 暂停 它。bg %1
: 让一个被暂停的作业(例如通过Ctrl+Z
)在 后台 继续运行。
关键理解: 这种 &
方式创建的后台任务与当前终端会话是 绑定 的。一旦关闭这个终端窗口,系统会发送一个 SIGHUP
(Hangup) 信号给该会话的所有子进程,导致它们全部终止。
10.3.2. 断线保护神:nohup
为了解决关闭终端会导致后台任务中断的问题,nohup
(No Hangup) 命令应运而生。它的核心作用就是 让进程忽略 SIGHUP
信号。
nohup
的标准用法几乎总是与 &
结合,并且强烈建议重定向输出流,否则所有输出都会默认写入到一个名为 nohup.out
的文件中。
语法: nohup <command> > <log_file> 2>&1 &
nohup <command>
: 使用nohup
来运行你的命令。> <log_file>
: 将标准输出(stdout)重定向到指定的日志文件。2>&1
: 将标准错误(stderr)重定向到与标准输出相同的地方。&1
表示“标准输出的文件描述符”。&
: 将整个命令放入后台执行。
实战场景: 我们有一个耗时的编译脚本 build.sh
,希望它在后台运行并将所有日志记录到 build.log
。
1 | # 以后台、忽略挂断信号的方式运行脚本,并合并所有输出到 build.log |
1
2
[1] 13579
nohup: ignoring input and appending output to 'build.log'
现在,即使我们关闭当前的终端,build.sh
进程(PID 为 13579)依然会在后台稳定运行,直到它自己执行完毕。我们可以随时通过 tail -f build.log
来实时监控它的进展。
nohup
非常适合那些启动后就不再需要交互的“一次性”批处理任务。但它的缺点也很明显:我们无法再回到那个任务的交互界面中去。如果需要一个可以随时“断开”和“重连”的持久化工作区,那么就需要下一节的终极武器 tmux
。
关键项 | 核心描述 | 注意事项 |
---|---|---|
command & | 将命令放入当前 Shell 的后台 临时 运行。 | 关闭 Shell 会导致任务终止。 |
jobs | 查看当前 Shell 会话中的后台作业列表。 | |
fg %<job_id> | 将后台作业调回前台。 | |
bg %<job_id> | 让一个已暂停的作业在后台继续运行。 | |
nohup command & | (推荐) 让命令 持久化 后台运行,忽略 SIGHUP 挂断信号。 | 强烈建议配合 > log_file 2>&1 使用,否则输出会写入 nohup.out 。 |
10.4. 任务持久化(二):终极武器 tmux
构建永不掉线的开发会话
在上一节中,我们学习了如何使用 nohup
来保护一个独立的、非交互式的后台任务。这对于执行批处理脚本非常有效。但现代开发工作流远不止于此:我们常常需要同时监控一个 Web 服务的日志、运行测试、并执行 Git 命令。nohup
无法为我们提供这样一个可随时离开并恢复的 交互式工作区。为此,我们需要 Linux 的终极武器——tmux
。
痛点背景: 想象一个典型的开发场景,你可能需要至少三个终端窗口:一个运行后端服务 npm run dev
,一个运行前端服务 vite
,第三个用于执行 git
命令或数据库查询。如果此时你需要重启电脑或切换到另一个项目,恢复这个工作布局将是一件繁琐的事情。tmux
允许我们创建一个可以随时“分离 (detach)”和“重连 (attach)”的虚拟工作空间,完美解决了这个问题。
10.4.1. 核心概念:tmux
的三层结构
tmux
的全称是 Terminal Multiplexer (终端复用器)。它的核心思想是在后台运行一个 tmux
服务进程,这个服务进程独立于任何终端窗口,并负责托管我们所有的工作会话。理解它的三层结构是掌握它的关键:
- 会话 (Session): 一个独立的、包含多个窗口的 **工作区**。你可以为每个项目创建一个 Session,例如一个用于 `project-a`,一个用于 `project-b`。
- 窗口 (Window): Session 内的 **标签页**。每个窗口都是一个全屏的 Shell 环境,你可以在一个窗口运行后端服务,在另一个窗口运行测试。
- 窗格 (Pane): 窗口内的 **分屏**。你可以将一个窗口垂直或水平分割成多个窗格,以便同时查看和操作多个命令。
10.4.2. 核心工作流:分离与重连
tmux
的所有快捷键都需要先按下一个 前缀键,默认为 Ctrl+b。操作模式是:先按下并松开 Ctrl+b,然后再按下一个功能键。
步骤一:安装并创建新会话
首先,请确保 tmux
已经安装。
1 | # 安装 tmux |
然后,创建一个名为 dev-session
的新会话。
1 | # 创建新会话 |
执行后,你会进入一个新的全屏界面,这标志着你已处于 tmux
的保护之下。
步骤二:在会话中工作并分离
现在,你可以在这个会话中运行任何耗时命令,例如启动一个开发服务器。
1 | npm run dev |
当你需要离开时,执行 tmux
的核心操作:分离会话。按下 Ctrl+b,松开,再按下 d (detach)。你会立刻返回到原来的终端,但 dev-session
及其中的任务仍在后台运行。
步骤三:查看与重连
你可以随时使用 tmux ls
命令,查看所有在后台运行的会话。
1 | # 列出所有 tmux 会话 |
1
dev-session: 1 windows (created Thu Sep 18 09:42:29 2025)
要重新进入会话,使用 attach
命令。
1 | # 重新连接到名为 dev-session 的会话 |
你会发现,之前运行的任务及其所有输出都原封不动地呈现在眼前,仿佛你从未离开过。
10.4.3. 效率提升:窗口与窗格管理
tmux
的强大远不止于持久化,它更是一个强大的窗口管理器。
窗格 (Pane) 操作
Ctrl+b
+ %
: 垂直分割当前窗格(左右分屏)。
Ctrl+b
+ "
: 水平分割当前窗格(上下分屏)。
Ctrl+b
+ <方向键>
: 在窗格之间进行导航。
Ctrl+b
+ x
: 关闭当前窗格。
窗口 (Window) 操作
Ctrl+b
+ c
: 创建一个新窗口 (create)。
Ctrl+b
+ w
: 列出所有窗口,并提供交互式选择 (windows)。
Ctrl+b
+ p
: 切换到上一个窗口 (previous)。
Ctrl+b
+ n
: 切换到下一个窗口 (next)。
Ctrl+b
+ <数字 0-9>
: 快速跳转到指定编号的窗口。
最佳实践: 为一个项目创建一个
tmux
会话。在 0 号窗口运行核心服务,并将其分割成服务日志和 Git 操作两个窗格。在 1 号窗口运行单元测试。在 2 号窗口连接数据库。这样就构建了一个功能完备且永不掉线的开发“驾驶舱”。
分类 | 快捷键/命令 | 核心描述 |
---|---|---|
会话管理 | tmux new -s <name> | 创建一个指定名称的新会话。 |
tmux ls | 列出所有后台运行的会话。 | |
tmux attach -t <name> | (最常用) 重连到指定名称的会话。 | |
Ctrl+b d | (最常用) 从当前会话中分离 (Detach)。 | |
窗口管理 | Ctrl+b c | 创建一个新窗口 (Create)。 |
Ctrl+b w | 列出并选择窗口 (Windows)。 | |
Ctrl+b p / n | 切换到上一个 (Previous) / 下一个 (Next) 窗口。 | |
窗格管理 | Ctrl+b % | 垂直分割窗格 (Vertical)。 |
Ctrl+b " | 水平分割窗格 (Horizontal)。 | |
Ctrl+b <方向键> | 在窗格间导航。 |
10.5. 网络侦探:解密端口占用之谜
通过 tmux
,我们已经为我们的开发工作流建立了一个坚不可摧的“堡垒”。在这个堡垒中,我们最常做的就是启动各种服务,尤其是 Web 服务。然而,当你满怀期待地敲下 npm run dev
或 docker-compose up
时,一个熟悉的错误往往会无情地跳出来:“Address already in use” 或 “Port 8080 is already in use”。
痛点背景: 端口被占用是开发过程中最常见的“拦路虎”之一。它可能是一个先前未被正常关闭的应用实例,一个后台僵死的进程,甚至是另一个你已经忘记了的项目。此时,我们迫切需要一个侦探工具,能立刻告诉我们:“究竟是哪个进程占用了这个端口?”
在 Linux 系统中,网络连接也被抽象为一种文件,因此,我们可以使用文件查看工具来一探究竟。
10.5.1. 首选利器:lsof
lsof
(List Open Files) 是一个功能极其强大的诊断工具。虽然名字是“列出打开的文件”,但配合 -i
参数,它就能化身为网络侦探。
语法: sudo lsof -i :<port_number>
sudo
: 查看网络端口信息通常需要 root 权限。-i
: 表示仅列出网络连接相关的“文件” (Internet sockets)。:<port_number>
: 指定你想要查询的端口号。
实战场景: 假设我们无法启动一个需要使用 8080
端口的应用。
1 | sudo lsof -i :8080 |
1
2
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 15234 prorise 123u IPv6 123456 0t0 TCP *:8080 (LISTEN)
lsof
的输出结果非常人性化,一目了然:
COMMAND
: 进程的命令名 (java
)PID
: 进程 ID (15234
)USER
: 运行该进程的用户 (prorise
)NAME
: 具体的连接信息,*:8080 (LISTEN)
表示它正在监听所有网络接口的 8080 端口。
现在,元凶已经找到!PID 为 15234
的 java
进程就是占用者。我们就可以用上一节学到的知识来处理它:kill 15234
。
10.5.2. 现代替代品:ss
ss
(Socket Statistics) 是一个更现代、更高性能的工具,专为查询网络套接字而设计,通常被认为是经典 netstat
命令的继任者。在系统繁忙时,ss
的查询速度通常比 lsof
更快。
语法: sudo ss -tlpn | grep <port_number>
-t
: 仅显示 TCP sockets。-l
: 仅显示正在监听 (Listen) 的 sockets。-p
: 显示使用该 socket 的进程信息。-n
: 以数字形式显示地址和端口,不做域名解析(这会更快)。
实战场景: 同样是调查 8080
端口。
1 | sudo ss -tlpn | grep 8080 |
1
LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("java",pid=15234,fd=123))
ss
的输出虽然信息密集,但也非常清晰,直接在 users
部分告诉了我们进程名 (java
) 和 PID (15234
)。
命令 | 核心描述 | 适用场景 |
---|---|---|
sudo lsof -i :<port> | (推荐) 列出打开指定网络端口的进程,输出可读性高。 | 日常开发、手动排查问题的首选。 |
sudo ss -tlpn | grep <port> | 更快、更现代的套接字统计工具,性能更优。 | 编写自动化脚本、或在超高负载服务器上排查。 |
10.6. 现代服务管理:systemd
与 systemctl
入门
痛点背景: 您在 WSL 中开发一个 Node.js 项目。通常的工作流是:打开 tmux
,进入项目目录,运行 npm start
,应用成功启动。但这种“手工作坊”式的管理方式,在稍微严肃一点的开发场景中,其弊端会立刻显现:
- 脆弱性: 应用因为一个未捕获的异常而崩溃。您不会收到任何通知,只能在发现服务无响应后,手动回到终端,重新执行
npm start
。 - 非持久性: 您关闭了 WSL 或重启了 Windows。当您再次打开终端时,必须记得到每个项目目录(后端、缓存服务等)去手动重启服务。这个过程极其繁琐且容易出错。
- 环境鸿沟: 在生产服务器上,运维人员绝不会用
tmux
或nohup
来运行核心应用。所有服务都由systemd
统一管理,以确保其健壮性和可维护性。您本地的开发方式与生产环境严重脱节,这会隐藏很多潜在问题。
systemd
和它的命令行工具 systemctl
,正是解决以上所有问题的专业方案。它能将您的应用提升为“一等公民”,像管理 Nginx、Docker、数据库一样,来管理您的应用程序。
10.6.1. 实战 DEMO:将 Node.js 应用服务化
让我们通过一个完整的例子,来体验 systemd
的威力。
第 1 步:准备一个简单的 Node.js 应用
假设我们有一个简单的 Express 应用。在您的 WSL 主目录中,创建一个新项目:
1 | # 创建项目目录并进入 |
现在,创建应用入口文件 app.js
:
1 | # 使用 cat 创建一个简单的 app.js 文件 |
现在,我们用**“老办法”**运行它:
1 | node app.js |
应用正在运行。但这很脆弱,关闭这个终端,应用就停止了。
第 2 步:编写 systemd
服务单元文件 (.service)
这是核心步骤。我们需要创建一个配置文件,告诉 systemd
如何启动、停止、重启我们的应用。这种配置文件被称为单元文件 (Unit File)。
所有用户自定义的系统服务单元文件,都应该放在 /etc/systemd/system/
目录下。
1 | # 使用 sudo 和您的首选编辑器创建一个新的服务文件 |
在编辑器中,粘贴以下内容。请务必根据您的实际情况修改 User
和 WorkingDirectory
。
1 | [Unit] |
关键配置项解析:
[Unit]
: 定义了服务的元数据和依赖关系。Description
是服务的简短描述,After
定义了启动顺序。[Service]
: 定义了服务的核心行为。User
/Group
: 出于安全考虑,服务不应该以root
用户运行。WorkingDirectory
: 这是执行命令前cd
到的目录,对于依赖相对路径的应用至关重要。ExecStart
: 启动服务的唯一命令。Restart=always
: 这是systemd
的“杀手级特性”。一旦进程因为任何原因(崩溃或被杀掉)退出,systemd
都会立即尝试重新启动它。
[Install]
: 定义了当服务被enable
(设置开机自启) 时的行为。
编辑完成后,按 Ctrl+X
,然后按 Y
和 Enter
保存并退出 nano
。
第 3 步:使用 systemctl
指挥您的服务
现在,我们的“菜谱”已经写好,是时候让“大厨” systemd
来烹饪了。
重新加载
systemd
配置
每当您创建或修改一个.service
文件后,都需要执行此命令,让systemd
重新读取其配置。1
sudo systemctl daemon-reload
启动您的服务
1
2sudo systemctl start my-app.service
执行后没有任何输出?这正是 Unix “没有消息就是好消息”的哲学。
1 |
|
您应该会看到类似下面的输出:
1
2
3
4
5
6
7
8
9
10
11
12
● my-app.service - My Awesome Node.js App
Loaded: loaded (/etc/systemd/system/my-app.service; disabled; vendor preset: enabled)
Active: active (running) since Thu 2025-09-18 14:30:00 UTC; 5s ago
Main PID: 12345 (node)
Tasks: 7 (limit: 4684)
Memory: 25.4M
CPU: 150ms
CGroup: /system.slice/my-app.service
└─12345 /usr/bin/node /home/your_username/my-awesome-app/app.js
Sep 18 14:30:00 my-wsl-machine systemd[1]: Started My Awesome Node.js App.
Sep 18 14:30:00 my-wsl-machine node[12345]: Example app listening on port 3000
从这里您可以看到服务是 `active (running)`,它的主进程 PID,以及**最新的几行日志**,这对于快速排错至关重要!
测试自动重启
打开另一个终端窗口,用curl
访问/crash
路由来模拟应用崩溃:1
curl http://localhost:3000/crash
现在,立刻回到第一个终端,再次检查服务状态:
1
sudo systemctl status my-app.service
您会惊奇地发现,服务的
Active
状态可能短暂地变为activating
,然后迅速回到active (running)
,但Main PID
已经变了!systemd
在检测到进程退出后,瞬间就把它拉起来了。这就是服务的健壮性。查看完整的服务日志
status
只显示最新几行日志。要查看全部或实时跟踪日志,请使用journalctl
。1
2
3
4
5# 查看 my-app 服务的所有日志
sudo journalctl -u my-app.service
# 实时跟踪日志 (类似 tail -f)
sudo journalctl -u my-app.service -f设置开机自启
现在服务已经可以稳定运行了,我们希望每次 WSL 启动时它都自动运行。1
sudo systemctl enable my-app.service
输出:
Created symlink /etc/systemd/system/multi-user.target.wants/my-app.service → /etc/systemd/system/my-app.service.
enable
命令本质上是创建了一个符号链接,告诉systemd
在启动时加载这个服务。对应的,取消开机自启的命令是:
1
sudo systemctl disable my-app.service
停止与重启服务
1
2
3
4
5# 停止服务
sudo systemctl stop my-app.service
# 重启服务 (通常在更新代码或配置后使用)
sudo systemctl restart my-app.service
10.6.2. 总结:从手工作坊到现代化运维的飞跃
通过上面的 Demo,我们已经完成了从手动到自动的质变。
对比维度 | 手动管理 (tmux /nohup ) | systemd 现代化管理 |
---|---|---|
启动方式 | 手动进入目录,执行 node app.js | sudo systemctl start my-app |
进程崩溃 | 进程死亡,服务中断,需手动重启 | 自动重启,服务自愈 |
系统重启 | 所有服务丢失,必须全部手动重开 | 自动启动 (如果 enabled ),服务持久化 |
状态监控 | ps aux | grep node ,信息零散 | systemctl status my-app ,状态、PID、内存、日志一目了然 |
日志查看 | 重定向到文件 > app.log 2>&1 ,需要手动管理 | journalctl -u my-app ,结构化、统一的日志系统 |
生产对齐 | 完全脱节 | 与生产环境完全一致的标准实践 |
掌握 systemd
和 systemctl
,意味着您已经开始用运维工程师的视角来思考和管理您的应用,这是从“能写代码”到“能部署和维护可靠服务”的关键一步。