1. 从装软件到杀进程:Linux 日常管理的两条主线
刚开始接触 Linux 的朋友,最容易卡住的地方往往不是“命令记不住”,而是搞不清自己在哪一层操作。比如同样是“装一个软件”——在 Windows 上你下载 exe 双击就行,但在 Linux 上,你可能遇到 apt-get install、dpkg -i、yum install、源码编译 ./configure && make && make install 好几种方式。再比如“关掉一个程序”——你可能会用 pkill、kill、kill -9,还可能顺手把终端直接关了,结果进程还在后台跑。这一章内容,就是帮你把“软件包管理”和“进程管理”这两条主线彻底拉通。
之所以把这两个话题放在一起讲,是因为它们在日常运维中总是纠缠出现——装软件装不上,要先排查进程是否占用了锁文件;某个服务卡死了,你得先会看进程状态,再决定是重启服务还是杀了重来。理解了这两块,Linux 基本命令这块就真正开始入行了。老规矩,这不是一本命令字典堆砌出来的文档,我会把每个命令背后的取舍、坑点、排查思路都掰开讲,让你看完之后不是“背下来”,而是“真的会用”。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 软件包管理:不只是“装得上”,还要“管得住”
2.1 先搞懂 Linux 软件包生态:apt、dpkg、yum、rpm 到底是什么关系
很多人第一次用 Linux 时,最大的困惑就是:为什么这个系统装软件这么麻烦?明明我在 Ubuntu 上能用 apt-get install 装 nginx,换了 CentOS 就得用 yum install nginx,再换一个发行版可能又要用 dnf 或者 zypper。实在让人崩溃。
这里得先给大家建立一个底层认知:Linux 软件分发的主流方式,不是单个可执行文件,而是“软件包”。一个软件包里面,除了程序本体,还包含它的配置文件、依赖关系信息、安装/卸载脚本等等。整个 Linux 世界里有两大软件包格式阵营:
- Debian 系(如 Ubuntu、Debian、Deepin、Kali):使用
.deb格式,底层工具是dpkg,上层自动解决依赖的工具是apt(apt-get / apt-cache)。 - RedHat 系(如 CentOS、RHEL、Fedora、Rocky):使用
.rpm格式,底层工具是rpm,上层自动解决依赖的工具从老牌的yum进化到了现在的dnf。
你要记住的核心逻辑是:dpkg/rpm 是“安装工人”,apt/yum/dnf 是“采购经理”。工人只负责干活,你把包给他,他就装;但如果你给的这个包还缺别的依赖包,工人不会帮你去找。而采购经理会自己去“软件仓库”里把需要的所有依赖都买齐,然后叫工人一起装好。
所以在实际工作中,你绝大多数情况下应该用 apt 或 yum/dnf,而不是直接 dpkg 或 rpm——除非你要安装一个下载好的本地包,比如 .deb 或 .rpm 文件。用 apt 装本地包是不行的,得用回 dpkg。
此外还有一个时代产物叫 snap,是 Ubuntu 力推的跨发行版打包格式,它在根目录下虚拟出一个只读的 squashfs 文件系统来隔离应用,好处是依赖打包得一干二净,坏处是装多了之后磁盘占用大、启动慢、有些应用和系统主题融合也一般。如果你只是想要“能装上、能用”,用系统自带的 apt/yum 足够了。
2.2 高频软件包操作速查:安装、卸载、升级、查询
我直接给一套我们自己服务器上常用的命令组合,这些命令基本覆盖了日常 90% 的场景。以 Debian/Ubuntu 系为例:
bash复制# 更新软件包索引(注意:不是升级软件本身!)
sudo apt update
# 升级所有可升级的软件包(保守一点,先看有哪些可升级)
apt list --upgradable
sudo apt upgrade
# 安装一个软件包,自动解决依赖
sudo apt install nginx
# 卸载软件包(不清理配置文件)
sudo apt remove nginx
# 彻底卸载(连配置文件一起清掉)
sudo apt purge nginx
# 清理无用的依赖包
sudo apt autoremove
# 搜索软件仓库里有没有这个包
apt search nginx
# 查看某个已安装软件包的详细信息
apt show nginx
# 查看某个软件包当前是否已安装
apt list --installed | grep nginx
# 只下载不安装
sudo apt download nginx
这里有个特别容易搞混的点:apt update 和 apt upgrade 的区别。很多新手一看“update”就以为是在升级系统,傻等半天发现啥也没装。实际上 apt update 是更新本地的“软件列表”,也就是让系统知道远程仓库里都有哪些版本的包、依赖关系是什么;apt upgrade 才是真正去下载并安装新版本。如果你改了软件源(比如把 Ubuntu 官方源换成国内镜像源),必须执行 apt update,否则系统仍然拿着旧的列表去下载,不仅速度慢,还可能 404。
RedHat 系的话,新系统用 dnf,老系统才是 yum,不过命令参数基本一样:
bash复制sudo dnf install nginx
sudo dnf remove nginx
sudo dnf search nginx
sudo dnf list installed | grep nginx
sudo dnf update
如果是拿到一个本地下载好的包,比如 google-chrome-stable_current_amd64.deb,就用 dpkg:
bash复制sudo dpkg -i google-chrome-stable_current_amd64.deb
但 dpkg 不会解决依赖,如果缺依赖会直接报错。所以更稳妥的姿势是:
bash复制sudo apt install ./google-chrome-stable_current_amd64.deb
注意在 apt install 后面直接跟本地文件路径,apt 会自动从本地包中解析依赖,并去仓库拉齐缺失部分。这是比 dpkg 更“现代”的做法。
2.3 软件包管理的三个隐蔽陷阱:锁定、破损依赖、软件源失效
这一节是纯经验教训,都是我在真实环境中踩过、也帮别人擦过屁股的坑。
陷阱一:Could not get lock /var/lib/dpkg/lock-frontend
这种报错十有八九是你的系统里同时跑了另一个包管理进程,比如你还开着 apt upgrade 在跑,又在另一个终端里执行了 apt install。解决方案是先看看是不是真的有进程在跑:
bash复制ps aux | grep -E "apt|dpkg"
如果确实有,等它跑完就好;如果是因为上次 apt 异常中断导致锁文件残留在那里,可以手动删除锁文件,但这招要慎用——只有在确认没有其他 apt/dpkg 进程在跑的情况下才能删。否则你删锁的同时另一个进程正在写系统状态,很容易搞出依赖状态不一致的系统。
bash复制sudo rm /var/lib/dpkg/lock-frontend
sudo rm /var/lib/dpkg/lock
sudo dpkg --configure -a # 修复中断的安装状态
sudo apt -f install # 修复损坏的依赖
陷阱二:软件包似乎无效 / 架构不匹配 / 无法定位软件包
“无法定位软件包”最常见的场景是——你明明知道这个软件存在,仓库源里却没有。原因通常是:
- 你忘了执行
apt update,本地索引太旧。 - 这个包不在系统默认源里,需要先添加第三方 PPA 或官方源。
比如装 Node.js,如果直接用 apt install nodejs,在很多 Ubuntu 版本上装出来的是一个很老的版本。我们一般会先添加 NodeSource 源:
bash复制curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
“架构不匹配”则说明你下载的包和你系统的 CPU 架构不符。先看看自己的架构:
bash复制dpkg --print-architecture
uname -m
x86_64 机器就别硬装 arm64 的包,这不是什么脑洞大开的技巧,纯粹是下载前看清文件名里的 amd64、arm64 标记。
陷阱三:虽然装上了,但版本和你要的不一样
Debian 系的 apt install 默认安装的是当前仓库里的最新版本,但这个“最新”是相对于该发行版发布时点的快照。比如你的 Ubuntu 22.04,仓库里的软件版本基本固定在了 2022 年前后,不会因为上游发布了新版就自动更新到仓库里。想要新版本,要么添加上游官方源,要么下载官方 .deb/.rpm 包,要么用 Docker 跑。所以“apt 装出来的不是最新版”不是 bug,是这种发行版的特性——追求稳定优先于追求新功能。
3. 进程管理:看清系统里到底在跑什么
3.1 进程的基础认知:PID、PPID、状态码
进程管理的第一步不是背命令,而是建立画面感:你运行的每个程序,在 Linux 眼里都是一个“进程”,系统给每个进程发了一个唯一的编号,叫 PID(Process ID)。进程之间还有父子关系,比如你在终端里跑 bash,bash 再启动 sleep 1000,那么 bash 就是父进程,sleep 就是子进程,PPID(Parent Process ID)记录的就是父进程的编号。
而在进程的一生中,它身处的状态也不是一成不变的。Linux 的进程状态常用的几个字母:
| 标志 | 含义 | 常见场景 |
|---|---|---|
| R | 运行中或可运行 | CPU 上正在跑或排队等待调度 |
| S | 可中断睡眠 | 等 I/O、等网络请求,能响应信号 |
| D | 不可中断睡眠 | 等磁盘 I/O,通常不能被杀掉,卡在这就很麻烦 |
| Z | 僵尸进程 | 子进程已结束,但父进程没回收它的退出状态 |
| T | 停止 | 被 Ctrl+Z 挂起或被 kill 暂停 |
僵尸进程是新手最容易困惑的东西。一看到 Z 就以为系统有病毒。其实僵尸进程并不是“还活着的坏东西”,而是子进程已经结束了,但父进程还没有读取它的退出码,所以系统保留了一条“尸体”记录。如果只是零星的僵尸进程,通常不用管,父进程退出后系统会让 init 进程统一回收。但如果僵尸进程大量堆积,就要重点排查是不是父进程代码写崩了、没有正常 wait 子进程。
3.2 ps、top、htop:三种看进程的方式各有什么侧重
ps 适合“快照式”看进程,top/htop 适合“持续监控”。
ps 命令其实是 System V 和 BSD 两套风格参数混搭,新手经常被搞晕。咱们别背那么多细节,记住两个最实用的组合:
bash复制# 查看当前终端下的进程
ps
# 查看所有进程,并显示完整命令行工作现场
ps aux
# 显示进程树:父子关系一目了然
ps -ef --forest
ps aux 输出的列里有一个 STAT 列,就是前面说的进程状态;%CPU 和 %MEM 是瞬时占用,仅供参考。还有一个 TIME 列表示进程累计消耗的 CPU 时间,不是程序运行了多久,别理解错了。
如果你要动态观察系统负载,用 top:
bash复制top
top 界面上按 P 按 CPU 排序,按 M 按内存排序,按 q 退出。真正带交互界面的更推荐 htop,它能横向滚动看完整命令行、支持鼠标点选、按 F5 看树形结构。装个一次性工具不算重,多花三分钟装 htop,日常排查效率提升非常大:
bash复制sudo apt install htop
3.3 前端任务 vs 后台任务:Ctrl+Z、jobs、bg、fg 怎么配合
实操中最常见的困惑不是“怎么看进程”,而是“我终端关了,进程是不是就没了”。这里得讲清楚前台、后台和守护进程的概念。
前台任务就是你直接在终端里运行的命令,比如 ping baidu.com,只要它不结束,你这个终端就干不了别的。
后台任务有两种启动方式。一种是在命令末尾加 &:
bash复制ping baidu.com > /tmp/ping.log 2>&1 &
这样 Ping 命令会转到后台执行,shell 会给你返回一个 [1] 12345,其中 12345 就是该任务的 PID。另一种是把正在前台跑的任务挂起再转后台:按 Ctrl+Z 把当前任务挂起,然后执行 bg 让它在后台继续运行。
用 jobs 可以查看当前 shell 管理的任务列表:
bash复制jobs
输出里 [1]+ 表示任务编号,Running 或 Stopped 表示状态,后面跟的是实际命令。如果你想把某个后台任务重新调回前台,用:
bash复制fg %1
这里有个非常大的坑你需要知道:如果你在本地终端启动了一个后台任务,然后把终端窗口直接关掉,这个进程十有八九会跟着终端一起挂掉。原因是终端关闭时会给会话里的进程发送挂断信号(SIGHUP)。正确做法是让进程脱离终端会话运行,比如用 nohup:
bash复制nohup ./my_service.py > nohup.out 2>&1 &
或者更现代的 systemd 服务方式。很多小白的服务器项目总是莫名其妙“断了连接进程就没了”,基本都是这个原因。还有一点:如果你用 & 启动后台任务后,又不想要它了,别急着暴力 kill,先用 jobs 看看任务号,然后 kill %1 按任务号杀更稳妥,不容易误杀同名的其他进程。
3.4 kill、pkill、killall:杀进程的正确姿势
说到杀进程,很多人一上来就学了一句 kill -9 PID,觉得这就是“终极大招”。实际上 -9 是最后的选择,不是首选。
kill 默认发的是 SIGTERM(15 号信号),意思是“兄弟,你收拾收拾准备结束”:程序可以借此机会保存数据、关闭文件、释放资源。而 kill -9 发的是 SIGKILL,意思是“立刻结束,没得商量”,内核直接收回进程的资源,不给程序任何清理机会。对大多数服务型程序来说,被 SIGKILL 干掉容易留下脏数据或未落盘的日志。
所以正确的杀进程姿势是这样的:
- 先看清楚的 PID:
ps aux | grep 我的服务 - 先发
kill PID,等一两秒 - 如果进程还在,再发
kill -9 PID
不过 kill 必须要指定 PID,如果你不知道 PID 只知道进程名字,也可以:
bash复制pkill -f nginx
killall nginx
pkill -f 是按命令行完整字符串去匹配,非常强大但也要小心:如果你执行 pkill -f my_service.py,它会匹配所有命令行里包含 my_service.py 的进程,如果别的进程恰好在同一个命令串里也有这个字符串,就会被误杀。线上操作时我建议先用 pgrep -af my_service.py 看看会匹配到哪些 PID,确认无误后再杀。
4. 实操联动:从“装包”到“管进程”的完整案例
4.1 场景一:部署 Nginx 后居然有两个 master 进程?
我们一步步走一遍“装软件 + 管进程”的联合操作。假设在一台全新的 Ubuntu 服务器上要装 Nginx。
bash复制sudo apt update
sudo apt install -y nginx
装完之后你想看看它到底起了什么进程:
bash复制ps aux | grep nginx
通常会看到两行以上输出,第一行是 master process,后面的若干行是 worker process。这是 Nginx 的多进程模型:master 负责管理配置、接收信号(比如 reload、quit),worker 真正干活的。所以在排查 Nginx 时,如果你只 kill 了一个 worker,master 会立刻再拉起一个新的 worker——你以为你杀成功了,其实服务一点没停。正确做法是:
bash复制# 平滑重载配置(推荐)
sudo nginx -s reload
# 如果一定要停止
sudo nginx -s stop
# 如果真出问题了,才考虑 kill master
sudo kill $(cat /var/run/nginx.pid)
所以千万不要看到一个 worker 进程不顺眼就直接 kill -9,那只会让 master 再换个 worker,压根没解决问题。这也是进程管理与软件包管理联动时最典型的场景:你得先理解这个软件的进程模型,再去操作它的进程。
4.2 场景二:服务真的挂死了,怎么纵横排查?
再模拟一个常见的线上事故:Nginx 服务异常,网站打不开,你想看看是不是进程出了问题。
第一步,确认进程在不在:
bash复制ps aux | grep nginx | grep -v grep
注意后面那个 grep -v grep,它把命令本身过滤掉。如果不加,你每次 grep 的时候都会匹配到自己这条 grep 命令,多出一行无意义的结果。
第二步,确认端口是否监听:
bash复制ss -tlnp | grep 80
ss 是 netstat 的现代替代品。如果没有输出,说明 Nginx 根本没监听到 80 端口,那大概率不是“卡死”而是“没起来”或“配置写错了”。
第三步,判断进程是否假死:
bash复制top -b -n 1 | grep nginx
如果 CPU 时间一直在涨但请求却打不通,那可能是 worker 卡在了上游请求上。这个时候不要盲目 kill,先看 Nginx 的错误日志,常见位置是 /var/log/nginx/error.log,里面有最直接的线索。
第四步,如果确认是神仙都救不回来的状态,再走“优雅结束 → 强制结束 → 确认清理”流程:
bash复制sudo nginx -s stop # 优雅停止
sudo pkill nginx # 如果优雅停止失败
sudo ss -tlnp | grep 80 # 再次确认端口是否释放
4.3 场景三:软件包装不上,先别急着重装系统
“软件包管理 + 进程管理”最常见的交叉问题,就是你在装一个包时发现一直报锁定错误,或者系统卡住不动。
我先说一个判断流程,这是我在多次救火中沉淀出来的排查顺序:
- 看锁:报错信息里是否出现
lock。出现就先查进程。 - 看依赖:报错是否出现“依赖”或
broken packages。锁定修复工具是apt -f install,它会把系统里所有处于“半安装”状态的包重新修正一遍。 - 看源:报错是否出现“404”或
Unable to fetch。说明软件源失效,常见于某些第三方源停服了。先把/etc/apt/sources.list里可疑的源注释掉,再apt update逐个排查。 - 看架构:报错是否出现
架构不匹配。对照dpkg --print-architecture检查包名。
很多时候你排查到最后会发现,根本不是软件包本身的问题,而是里头的进程一直占着锁或端口不放。比如你之前装 MySQL 失败了,但 mysqld 进程还在后台跑着,你去重装时系统发现“我靠,你已经在跑着一个了”,就会提示包配置失败。
所以一个比较粗暴但非常有效的思路是:把相关进程全部停下来,把包卸干净,再重装:
bash复制# 以 MySQL 为例
sudo systemctl stop mysql
sudo pkill mysqld
sudo apt purge mysql-server mysql-client
sudo apt autoremove
sudo apt install mysql-server
这套“进程先停→包卸干净→重新安装”的思路,适用于 MySQL、PostgreSQL、Docker 等一大堆服务型软件。
5. 高频报错速查表:软件包与进程的实战排查手册
这一节我把自己踩过、以及帮人排查过的典型报错,整理成一个速查表。每个报错都附上我的处理经验,核心思路是:先判断这是“包环境问题”还是“进程运行问题”,再决定处理手段。
| 报错/现象 | 所属类型 | 最快排查路径 | 处理要点 |
|---|---|---|---|
Could not get lock /var/lib/dpkg/lock |
包管理 | 检查 `ps aux | grep apt` |
软件包似乎无效 |
包管理 | 检查是否用了 dpkg -i 装本地包 |
用 apt install ./xxx.deb 替代 |
无法定位软件包 |
包管理 | apt update 后再试 |
检查是否添加了正确的第三方源 |
软件包架构不匹配 |
包管理 | dpkg --print-architecture |
下载对应架构的包 |
依赖关系不完整 |
包管理 | sudo apt -f install |
修复半安装状态的包 |
| 服务配置文件改了不生效 | 进程 | 确认有没有加载新配置 | 用 systemctl reload 而非 restart |
| 端口明明没进程监听却提示占用 | 进程 | ss -tlnp 查看所有监听 |
可能是 SO_REUSEPORT 或多实例 |
| 终端关闭后进程消失 | 进程 | 确认是否用了 nohup 或 systemd |
长期任务必须脱离终端会话 |
| 僵尸进程很多 | 进程 | 查看父进程 PID | 重点排查父进程是否异常 |
kill -9 之后端口还占着 |
进程 | ss -tlnp 看占用进程 |
可能变成 TIME_WAIT,等待内核回收 |
Terminated 后系统没有任何提示 |
进程 | 检查进程状态码 | 部分守护进程被杀后会自动拉起,不是 bug |
这里特别展开讲讲 TIME_WAIT 这个点。你 kill 掉了一个进程,但 ss -tlnp 一看端口还占着,连接状态是 TIME_WAIT。这其实不是进程还在,而是 TCP 协议栈为了保证网络上延迟的数据包不被新连接复用,必须等 2MSL 超时(一般是 60 秒左右)。遇到这种情况,不用焦虑,等一会儿端口自然释放。有些教程教人改 sysctl 参数调低 TIME_WAIT 回收时间,但生产环境不建议乱改,内核参数都是几十年的经验结晶,改之前必须搞明白每个参数的含义。
另一个常见现象是:systemctl stop nginx 之后,你又执行了 ps aux | grep nginx,发现居然还有进程。这可能是因为 systemd 有自动重启策略:如果服务的 Restart=always 被配置了,systemd 会在进程退出后自动拉起。你以为你在杀进程,实际上 systemd 在疯狂守护,两边在拉锯战。正确的做法是:
bash复制sudo systemctl stop nginx
必要时再禁用自启:
bash复制sudo systemctl disable nginx
所以在你 pkill 之前,永远是那句老话:先搞清楚这个进程归属于谁、由谁拉起。系统级的看 systemd,用户级的看父进程,然后你再决定是“杀掉这个进程”还是“把这个服务停掉”。
6. 我的作业级总结:如何把这些命令内化成自己的技能
写到这里,我想分享一点个人的学习心得。这本书(或者说这份教程)讲了这么多 Linux 基本命令,但很多人看完就忘,根本原因是学习方式不对——他们是“浏览”,而不是“操作”。
我给一个非常实用的建议:别在虚拟机里跟着鼠标点来点去,也别只看别人敲命令。直接开一台几块钱一个月的云服务器,或者本地用 VirtualBox 开个 Ubuntu Server,每天给自己布置一个小任务。比如今天的目标是“用 apt 装一个 nginx,然后把它改造成一个静态文件服务器”。明天是“写一个小脚本,让它跑在后台,用 ps 观察它的状态,再用 kill 把它结束掉”。七天之内这组操作你重复过三五遍之后,肌肉记忆自然就形成了,根本不需要刻意背。
还有一个我个人踩了无数坑之后的体会:命令参数不用记优先级,但“先查后杀”的排查思路你必须刻在骨子里。无论遇到软件包问题还是进程问题,不要上来就复制网上搜到的 rm -rf 或者 kill -9,先花三十秒用 ps、apt-cache policy、ss 这些“查看类”命令搞清楚现状,再决定下一步动作。多数生产事故不是“命令用错了”,而是“没看清状态就动手”。
最后再送一个小技巧:给常用命令配个别名,或者写成一个小脚本。比如我自己在 .bashrc 里加了这么一行:
bash复制alias pg='ps aux | grep -v grep | grep -i'
之后查看任何进程,只需要 pg nginx 就够了,省得每次敲那么长一串。你也可以根据自己常用的软件把 apt install 和 systemctl start 包装成自己的快捷函数。这样做的意义不只是省时间,更重要的是,你在“定制命令”的过程中已经把这个命令的原理彻底搞明白了。
