Linux Luminarium-进程与任务模块
“进程与任务” (Processes and Jobs) 模块
在 Linux 的学习旅程中,你经常会遇到需要管理程序运行的情况。计算机通过执行软件来完成各项工作。在现代计算领域,这些软件主要分为两大类:操作系统内核(我们将在后续课程中深入学习)和进程(Processes),后者正是本模块讨论的核心
当 Linux 系统启动时,它会首先启动一个名为 **init**(初始化进程的缩写)的进程。随后,init 会启动一系列其他进程,这些进程又会进一步产生更多的子进程。这一链式反应持续进行,直到最终呈现出我所使用的命令行 Shell —— 实际上,Shell 本身也是一个进程。
当然,当我输入命令时,Shell 也会启动相应的进程来响应我的指令。在本模块中,将学习如何通过多种方式查看并与这些进程进行交互。
利用ps命令列出正在运行的进程
基础知识:什么是 PID 和 ps?
- PID (Process ID):每一个运行中的进程都有一个唯一的数字标识符,称为进程 ID。
- **默认 **
**ps**:如果只输入ps,它仅显示当前终端窗口(TTY)中启动的进程。通常只能看到bash(Shell 本身)和ps命令。
由于 ps 是一个历史悠久的工具,它支持两套略有不同的参数语法,这两套命令在实际工作中最为常用:
| 语法类型 | 常用命令 | 参数解析 | 侧重点 |
|---|---|---|---|
| 标准语法 (Standard) | ps -ef |
-e: 所有进程-f: 完整格式显示 |
PPID (父进程 ID),方便追踪谁启动了谁。 |
| BSD 语法 | ps aux |
a: 所有用户进程x: 包含无终端进程u: 用户易读格式 |
CPU 和内存占用百分比 |
hacker@dojo:~$ ps
PID TTY TIME CMD
329 pts/0 00:00:00 bash
349 pts/0 00:00:00 ps
hacker@dojo:~$
在上面的例子中,我们有 shell(bash)和 ps 进程本身,这就是在该特定终端上运行的全部。我们还看到每个进程都有一个数字标识符(Process ID,或 PID),这是一个唯一标识 Linux 环境中所有运行进程的数字。我们还可以看到执行命令的终端(这里是 pts/0 的指定),以及进程迄今为止消耗的 CPU 总时间
hacker@dojo:~$ ps -ef
UID PID PPID C STIME TTY TIME CMD
hacker 1 0 0 05:34 ? 00:00:00 /sbin/docker-init -- /bin/sleep 6h
hacker 7 1 0 05:34 ? 00:00:00 /bin/sleep 6h
hacker 102 1 1 05:34 ? 00:00:00 /usr/lib/code-server/lib/node /usr/lib/code-server --auth=none -
hacker 138 102 11 05:34 ? 00:00:07 /usr/lib/code-server/lib/node /usr/lib/code-server/out/node/entr
hacker 287 138 0 05:34 ? 00:00:00 /usr/lib/code-server/lib/node /usr/lib/code-server/lib/vscode/ou
hacker 318 138 6 05:34 ? 00:00:03 /usr/lib/code-server/lib/node --dns-result-order=ipv4first /usr/
hacker 554 138 3 05:35 ? 00:00:00 /usr/lib/code-server/lib/node /usr/lib/code-server/lib/vscode/ou
hacker 571 554 0 05:35 pts/0 00:00:00 /usr/bin/bash --init-file /usr/lib/code-server/lib/vscode/out/vs
hacker 695 571 0 05:35 pts/0 00:00:00 ps -ef
hacker@dojo:~$
可以看到,有进程用于初始化挑战环境(<font style="color:rgb(31, 31, 31);">docker-init</font>)、自动终止前的超时以保留计算资源(6 小时后睡眠到超时)、VSCode 环境(多个代码服务器辅助进程)、<font style="color:rgb(31, 31, 31);">shell</font>(bash)以及我的 <font style="color:rgb(31, 31, 31);">ps -ef </font>命令。<font style="color:rgb(31, 31, 31);">ps aux </font>基本上也是一样的道理:
hacker@dojo:~$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
hacker 1 0.0 0.0 1128 4 ? Ss 05:34 0:00 /sbin/docker-init -- /bin/sleep 6h
hacker 7 0.0 0.0 2736 580 ? S 05:34 0:00 /bin/sleep 6h
hacker 102 0.4 0.0 723944 64660 ? Sl 05:34 0:00 /usr/lib/code-server/lib/node /usr/lib/code-serve
hacker 138 3.3 0.0 968792 106272 ? Sl 05:34 0:07 /usr/lib/code-server/lib/node /usr/lib/code-serve
hacker 287 0.0 0.0 717648 53136 ? Sl 05:34 0:00 /usr/lib/code-server/lib/node /usr/lib/code-serve
hacker 318 3.3 0.0 977472 98256 ? Sl 05:34 0:06 /usr/lib/code-server/lib/node --dns-result-order=
hacker 554 0.4 0.0 650560 55360 ? Rl 05:35 0:00 /usr/lib/code-server/lib/node /usr/lib/code-serve
hacker 571 0.0 0.0 4600 4032 pts/0 Ss 05:35 0:00 /usr/bin/bash --init-file /usr/lib/code-server/li
hacker 1172 0.0 0.0 5892 2924 pts/0 R+ 05:38 0:00 ps aux
hacker@dojo:~$
<font style="color:#000000;background-color:rgba(255, 255, 255, 0.03);">ps -ef</font> 和 <font style="color:#000000;background-color:rgba(255, 255, 255, 0.03);">ps aux</font> 有许多共通点:两者都显示用户(<font style="color:#000000;background-color:rgba(255, 255, 255, 0.03);">USER</font> 列)、PID、TTY、进程的启动时间(<font style="color:#000000;background-color:rgba(255, 255, 255, 0.03);">STIME</font>/<font style="color:#000000;background-color:rgba(255, 255, 255, 0.03);">START</font>)、总使用的 CPU 时间(<font style="color:#000000;background-color:rgba(255, 255, 255, 0.03);">TIME)</font> 和命令(<font style="color:#000000;background-color:rgba(255, 255, 255, 0.03);">CMD</font>/<font style="color:#000000;background-color:rgba(255, 255, 255, 0.03);">COMMAND</font>)。 <font style="color:#000000;background-color:rgba(255, 255, 255, 0.03);">ps-ef</font> 还输出父进程 ID(<font style="color:#000000;background-color:rgba(255, 255, 255, 0.03);">PPID),</font> 即启动该进程的 PID,而 <font style="color:#000000;background-color:rgba(255, 255, 255, 0.03);">ps aux</font> 输出进程在系统 CPU 和内存中所占比例。另外,还有很多其他内容
题目将 /challenge/run 重命名为了一个随机文件名,并且禁止你查看 /challenge 目录的内容。但因为这个程序正在运行,你可以通过进程列表找到它。
操作步骤:

利用kill命令终止进程
在 Linux 系统中,终止进程是通过 kill 命令实现的。尽管其名称显得激进,但其本质是向进程发送信号(Signals)。
信号机制与 PID
默认情况下,执行 kill 命令会发送一个名为 SIGTERM 的信号。这相当于给进程发送一个“请关闭”的正式请求,允许进程在退出前保存数据或清理临时文件。
与许多文本处理命令不同,kill 不直接接受进程名称作为参数,它必须配合 PID(进程标识符) 使用。
hacker@dojo:~$ sleep 1337
hacker@dojo:~$ ps -e | grep sleep
342 pts/0 00:00:00 sleep
hacker@dojo:~$ kill 342
hacker@dojo:~$ ps -e | grep sleep
hacker@dojo:~$
现在有一个进程(/challenge/run)无法被运行,需要你先终止它再运行

利用Ctrl+C中断
有时只是想摆脱占用终端资源的进程,可以使用快捷键(按住CTRL并按下C)会向任何正在等待终端输入的应用程序发送“中断”,通常情况下,这个会导致应用程序的正常退出
比如:

资源占用问题
命名管道和进程竞争
命名管道(FIFO)在 Linux 中就像一根物理管道,通常一次只能由一个进程进行有效的写入管理。如果一个“诱饵”进程(Decoy)已经打开并持续向管道内灌入垃圾数据,/challenge/run 就无法顺利完成它的任务。
首先先识别这个命名管道找到/challenge/decoy 的进程PID 使用ps -ef | grep decoy
找到PID之后使用kill强制结束这个进程
等待缓冲区清空
由于 Linux 的管道具有”缓冲(Buffered)"机制,即使进程被杀掉,管道里可能还残存着一些已经发出的“垃圾数据”。
- 可以运行
cat /tmp/flag_fifo来观察。 - 如果屏幕上还在跳动干扰信息,稍等几秒钟,直到数据流停止。
当管道清空的时候且没有进程干扰,就运行主程序:
/challenge/run
然后读取cat /tmp/flag_file
使用CTRL+Z暂停进程
将后台进程挂起
当你按下 Ctrl-Z 时,终端会向当前前台进程发送一个 SIGSTOP 信号。
- 状态切换:进程并没有消失,而是进入了“停止(Stopped)”状态。它会释放对终端控制权的占用,但依然驻留在内存中。
- 恢复可能:挂起的进程就像被按下了暂停键的电影,随时可以被恢复运行(我们将在下一关学习如何恢复)。

利用fg恢复进程
当挂起进程后,要怎么做才可以恢复它,一个内置命令fg可以获取挂起的命令

先创建一个进程挂起然后用命令fg恢复
利用命令bg将进程转入后台
Linux 终端中,如果你希望一个程序在运行的同时不占用你的命令行界面,你可以使用 **bg** 命令(Background 的缩写)将其转入后台运行。
前台、挂起与后台的状态切换
- 前台 (Foreground):程序正在运行并占据你的屏幕,你无法输入其他命令。
- 挂起 (Suspended/Stopped):通过
Ctrl-Z实现,程序暂停所有动作,像“断电”一样停留在内存中。 - 后台 (Background):程序正在持续运行,但不占用终端。你可以继续输入命令,而它在“幕后”工作。
如何观察状态差异(Arcanum) :
可以通过 ps 命令配合 -o 选项来观察进程状态(STAT)的细微变化
首先,我们挂起一个 sleep 进程:
hacker@dojo:~$ sleep 1337
^Z
[1]+ Stopped sleep 1337
此时 sleep 进程在后台处于挂起(suspended)状态。我们可以通过启用 stat 列来查看:
hacker@dojo:~$ ps -o user,pid,stat,cmd
USER PID STAT CMD
hacker 702 Ss bash
hacker 762 T sleep 1337
hacker 782 R+ ps -o user,pid,stat,cmd
**T**:代表该进程因我们按下了Ctrl-Z而被挂起/停止。**S**(在 bash 行):代表 bash 正在睡眠并等待输入。**R**(在 ps 行):代表它正在活跃运行。**+**符号:代表该进程处于前台。
现在,让我们在后台恢复这个 sleep 进程:
hacker@dojo:~$ bg
[1]+ sleep 1337 &
hacker@dojo:~$ ps -o user,pid,stat,cmd
USER PID STAT CMD
hacker 702 Ss bash
hacker 762 S sleep 1337
hacker 1224 R+ ps -o user,pid,stat,cmd
看!sleep 进程的状态现在变成了 **S**。它虽然是在“睡眠”,但它不再是挂起状态了!同时,因为它在后台运行,所以它的状态码后面没有 **+** 符号。
按照上述“启动 -> 挂起 -> 后台化 -> 再次启动”的流程操作,即可通过检测并获取 Flag。

挂起、后台化、再恢复

直接启动后台进程
之前的关卡中,你学会了如何将正在运行的进程“推”到后台。但实际上,你并不需要先启动再挂起,可以在命令开始时就直接让它在后台运行。
使用 & 符号直接启动
在 Linux 命令的末尾添加一个 **&** 符号,shell 就会在后台启动该程序,并立即把控制权还给你,让你能继续输入其他命令。
- 即时反馈:启动后,终端会显示类似
[1] 1771的信息。[1]是作业号 (Job ID)。1771是该进程的 PID。
hacker@dojo:~$ sleep 1337 &
[1] 1771
hacker@dojo:~$ ps -o user,pid,stat,cmd
USER PID STAT CMD
hacker 1709 Ss bash
hacker 1771 S sleep 1337
hacker 1782 R+ ps -o user,pid,stat,cmd
hacker@dojo:~$
现在尝试一下/challenge/run

退出码
在 Linux 中,每一个命令在运行结束时都会向系统报告一个退出码(Exit Code)。这个数值用于告知用户或 Shell 该程序是圆满完成了任务,还是中途遇到了错误。
退出码的基本规律
**0**:代表成功。程序按预期运行并顺利结束。- 非
**0**(通常是**1**到**255**之间的整数):代表失败。不同的数字有时代表特定的错误类型(例如权限不足、找不到文件等)。
如何读取退出码:$? 变量
Shell 提供了一个特殊的变量 **$?** 来存储最近一次执行完的命令的退出码。要查看它的值,你需要使用 echo 命令并在变量名前加上 $ 符号。
让我们看原文中的例子:
成功案例:
hacker@dojo:~$ touch test-file
hacker@dojo:~$ echo $?
0
因为 touch 成功创建了文件,退出码为 0。
失败案例:
hacker@dojo:~$ touch /test-file
touch: cannot touch '/test-file': Permission denied
hacker@dojo:~$ echo $?
1
由于没有权限在根目录下创建文件,touch 报错,退出码变为了 1。
时效性:$? 永远只保留紧接着的前一个命令的退出码。如果你在运行 /challenge/get-code 之后不小心运行了 ls,那么 echo $? 显示的就是 ls 的结果了。若发生这种情况,请重新运行一次 /challenge/get-code。

本练习: 获取一个程序的退出码,并将其作为另一个程序的参数。

浙公网安备 33010602011771号