去年秋天帮一个客户排查服务器故障,顺手把排查过程记在了笔记里。后来这个笔记越写越长,从一条命令到一段内核报错,从新机器初始化到老机器救火,慢慢攒了不少东西。今天把最有代表性的内容整理出来,聊几个常见的 Linux 使用场景,有些是新手容易卡住的坎,有些是运维老手也可能忽略的细节。内容偏向实际动手,每个知识点我都会说明当时是为什么踩进去的,以及后来怎么绕出来的。
1. 从一次磁盘告警说起:Linux命令不是背的,是拿来救命的
先说个典型的场景。客户那边报警说数据库服务器的磁盘空间快满了,登录上去执行 df -h 一看,根分区确实红了。但顺着 du -sh /* 往下排查的时候,却发现各个目录加起来的总量明显小于磁盘总占用——这中间差了好几十 GB。如果你也遇到过这种情况,说明有文件被删除了,但进程还在持有句柄,空间没有真正释放。
这时候 lsof | grep deleted 能直接帮你找到是哪个进程还拽着那个文件。找到 PID 之后重启对应服务,或者干脆 kill 掉,磁盘空间就回来了。这条命令平时没人会觉得重要,但真正碰到一次线上告警,比背一百遍命令列表都管用。
另外一个高频场景是删文件。很多人习惯 rm -rf 一条命令走天下,但遇到大量小文件的时候,rm 的性能非常差,比如要删除一个包含几万个图片缩略图的目录,rm 可能要跑几分钟。这种场景下我一般用 rsync 配合一个空目录来清空,rsync -a --delete empty_dir/ target_dir/ 的执行速度会比 rm 快很多。原因是 rsync 的删除逻辑走的是批量目录项清理,而 rm 是逐个文件 unlink,两者的系统调用开销差距很大。
排查类似问题的时候还需要警惕动态数据目录的干扰。比如 /var/log、/tmp、/home 这仨目录经常是藏日志和大文件的重灾区。我见过有人把 Docker 的 overlay2 目录直接扔在根分区,结果镜像和容器日志一路涨到磁盘爆满。所以检查的时候不要只看一级目录,要逐层往下走,特别是 /var/lib/docker、/opt 这类第三方应用常用的安装路径,很容易被忽略。
在排查过程中,watch 命令也经常派上用场。分析日志增长是否还在继续时,用 watch -n 1 'df -h /' 每秒刷新一次磁盘占用,能直观看到空间有没有持续下降。如果你要定位某个正在被大量写入的文件,可以按 lsof +L1 找出所有被删除但仍被打开的文件,再结合文件大小排序,基本十秒钟锁定目标。
我个人的习惯是遇到任何异常先开一个终端原地记录执行过的命令和输出,另一个终端做操作。这样不管是回溯问题还是写故障报告,都有据可查。只靠脑子里那几条命令到处试,很容易越弄越乱。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 用户与权限:为什么团队里总是有人 sudo 完就出事儿
很多从 Windows 转过来的同事,最开始接触 Linux 都习惯直接切换到 root 操作。虽然省事,但随之而来的权限问题会比想象中多得多。新建一个用户、配置 sudo、修改文件属主,这些操作用到的命令不多,但背后的模型和 Windows 差别很大,值得单独捋一遍。
新建用户这个动作,很多人只记得 useradd 加一个用户名,但忘了指定家目录和 shell。比如执行 useradd testuser 之后,系统默认创建的家目录可能是 /home/testuser,但默认的 shell 可能是 /bin/sh,也可能因为 umask 或 skel 配置的问题,家目录里缺少常规的 .bashrc、.bash_profile 文件。结果用户第一次登录进去,命令提示符长得奇怪,补全和别名也没有,新手一下就懵了。
正确的做法是创建用户时一次性指定完整信息:
bash复制useradd -m -d /home/testuser -s /bin/bash testuser
passwd testuser
-m 表示同时创建家目录,-d 指定家目录路径,-s 指定登录 shell。如果你希望新用户能执行 sudo 命令,需要把用户加入 wheel 组或者编辑 /etc/sudoers 文件。很多人直接去改 /etc/sudoers,结果语法错误导致 sudo 完全不可用,最后只能进单用户模式修复,非常被动。安全的做法是用 visudo 命令打开编辑,它会在保存时帮你做语法检查,避免把自己锁在外面。
权限这块,我见过一个特别典型的坑:项目目录已经用 chown -R appuser:appgroup /data/project 授权了,但某个子目录就是从代码仓库里带着可执行权限传上来的,或者反过来,某个脚本明明有执行权限,却因为挂载时用了 noexec 参数没法启动。这种问题查权限表是查不出来的,得检查挂载参数。建议对权限有疑问时先跑 mount 和 df -T 看文件系统类型与挂载选项,再决定是不是权限本身的问题。
sudo 的配置同样有讲究。比如想允许某个用户只执行特定的几条命令,可以在 /etc/sudoers.d/ 下放一个单独的文件:
bash复制testuser ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx
这样既满足了运维需要,又不需要把整个 root 权限交出去。团队内部如果人多,还可以结合用户组统一管理。把一批需要部署权限的人加入 devops 组,然后给组配置特定命令的 sudo 权限,比挨个用户给 root 权限要安全得多,审计日志里也能分清是谁执行了什么操作。
权限的排查除了 ls -l 之外,还有一个工具叫 namei,可以逐层解析路径中每一级目录的权限。比如你发现一个文件明明权限是 777,普通用户还是访问不了,原因往往是某个上级目录缺少 x 权限。namei -l /data/project/log/app.log 会把你路径上每一层的权限都列出来,这种问题一眼就能定位。
3. 别再把文件从一个机器拖到另一个机器搞那么复杂了
Windows 和 Linux 互相传文件是老生常谈,但从实际问的频率来看,一直有人在这方面踩坑。最常见的两种需求,一种是直接从 Linux 命令行把文件拉到本地 Windows,另一种是反过来把 Windows 上的文件推给 Linux 服务器。
第一种场景用 scp 是最快的:
bash复制# 从 Linux 服务器下载文件到当前 Windows 终端目录
scp username@server_ip:/data/backup/backup.tar.gz .
# 上传本地文件到服务器的 /tmp 目录
scp backup.tar.gz username@server_ip:/tmp/
scp 走的是 SSH 协议,端口默认 22,只要服务器能 SSH 登录,这个命令就能用。唯一要提醒的是如果服务器改了 SSH 端口,要记得加 -P 参数指定端口,大写 P。小写 -p 在 scp 里是保留文件修改时间的,经常有人搞混。
说到 Windows 和 Linux 之间共享文件,如果两台机器在同一个局域网里,我一般更推荐用 Samba 搭一个共享目录,Windows 资源管理器直接访问 \服务器IP\share 就能读写。但 Samba 配置有一个高频出错点:SELinux 或防火墙会把 Samba 的访问挡掉。所以配置好共享之后,第一件事要确认 firewalld 是否放行了 samba 服务,以及 SELinux 是否设置了对 samba 目录的上下文。
bash复制# 放行防火墙
firewall-cmd --permanent --add-service=samba
firewall-cmd --reload
# 设置 SELinux 文件上下文
semanage fcontext -a -t samba_share_t /data/share
restorecon -Rv /data/share
如果你不需要常驻共享,只是偶尔传一次文件,其实还有更轻的办法:在 Windows 上用 Python 临时起一个 HTTP 服务,Linux 端用 wget 拉取。比如在 Windows 某个文件夹里打开命令行执行 python -m http.server 8000,然后 Linux 端 wget http://windows_ip:8000/文件名 就能下载。反过来,在 Linux 端执行 python3 -m http.server 8000,Windows 浏览器也能直接下载 Linux 上的文件。这个方法特别适合临时传单个安装包,不用折腾 Samba。
WSL 环境则有一些特殊情况。比如我在 WSL 里经常需要访问 Windows 的 D 盘,挂载路径是 /mnt/d,反过来 Windows 访问 WSL 内部文件可以在资源管理器地址栏输入 \wsl$\,里面能看到各个发行版的目录。需要注意 Windows 和 WSL 之间的文件访问性能目前仍有瓶颈,大量小文件读取会比原生文件系统慢不少,所以跨系统开发的时候,把代码放在 Linux 原生文件系统里能明显改善操作流畅度。
4. 从零搭建一台能长期运行的 Linux 服务器:网络与基础环境
不管是个人使用还是公司测试环境,装完 Linux 系统之后的第一件事通常都是配置网络。很多人在图形界面里点几下就能上网,但一到服务器或者虚拟机就卡在固定 IP 配置上,尤其是从 Ubuntu 切到 Rocky Linux 这类基于 RHEL 系的发行版,网卡命名方式和配置文件路径完全不一样。
Rocky Linux 目前主要用 NetworkManager 管理网络。设置静态 IP 有两种方式,一种是用 nmcli 命令行工具,另一种是直接编辑 /etc/NetworkManager/system-connections/ 下对应的网卡配置文件。我推荐用 nmcli,因为不会因为配置文件格式写错导致网络服务直接挂了。
bash复制# 查看当前网卡和连接名
nmcli connection show
# 修改为静态 IP
nmcli connection modify "ens160" ipv4.method manual ipv4.addresses 192.168.1.100/24 ipv4.gateway 192.168.1.1 ipv4.dns "223.5.5.5 8.8.8.8"
# 激活配置
nmcli connection up "ens160"
做好这一步之后,最好检查一下 /etc/resolv.conf 是否正常生成了 DNS 配置。有时候你明明配置了 DNS,但系统里跑着 systemd-resolved,它会自己管理这个文件,手动改了也会被覆盖。如果 ping 域名不通但 ping IP 正常,可以先确认 systemd-resolved 的状态,再用 resolvectl status 查看实际生效的 DNS。
配置好网络之后,基础环境里比较常见的是装 Python 和多版本管理。Linux 系统自带的 Python 版本通常偏老,比如 CentOS 7 自带的是 Python 2.7,而 Rocky Linux 9 自带的是 Python 3.9。很多项目需要更高版本,这时候我一般用 pyenv 管理多版本,而不是直接动系统自带的 Python。
pyenv 的核心思路是编译安装,所有版本都放在 ~/.pyenv 下面,切换版本只影响当前用户的 PATH,不会污染系统环境。安装依赖的时候要注意提前装好编译工具链和常见依赖库,否则编译过程会报各种头文件缺失:
bash复制dnf groupinstall "Development Tools"
dnf install openssl-devel bzip2-devel libffi-devel readline-devel sqlite-devel
另外提醒一点,在服务器上创建虚拟环境时,很多教程会写 python -m venv,但如果你是用 pyenv 切换的版本,一定要确认 python 指向的是当前 pyenv 版本。反复遇到 ModuleNotFoundError 时,先检查 which python,再检查 pip list,很多时候是环境变量串了。
网络和应用部署方面,Nginx 是绕不开的一个环节。安装本身不复杂,dnf install nginx 或者 apt install nginx,装完 systemctl enable --now nginx 就行。但真正容易出问题的有三个地方:一是默认站点配置文件 /etc/nginx/conf.d/default.conf 和 /etc/nginx/sites-enabled/ 的关系搞不清楚;二是 server_name 和 listen 的匹配逻辑没弄明白,导致访问域名却打开默认页;三是代理配置里忘了加 Host 头,导致后端应用生成错误的回调地址。
举个例子,如果你配了一个反向代理:
nginx复制server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
看起来没问题,但如果你忘了 proxy_set_header Host $host 这一行,后端应用拿到的 Host 头是 127.0.0.1:8080,生成的重定向 URL 就会变成内网地址,用户点击跳转就出问题。这种问题排查起来不复杂,但非常容易让人一头雾水。遇到这类问题先 curl 试探一下后端返回头,再检查代理配置,基本都能解决。
如果你是给公司内部批量装系统,可能还会接触到 Cobbler 这类自动化部署工具。Cobbler 的核心思路是用 PXE 引导加 Kickstart 应答文件来实现无人值守安装。它的配置涉及 dhcp、tftp、http 和镜像同步,一个典型的坑是启动之后发现客户端找不到引导文件,比如出现 PXE-E32 之类的报错,原因可能是 tftp 的根目录路径不对,或者思科交换机上没开 ip helper-address。建议先把 tftp 服务器和 dhcp 的 next-server 参数对齐,再确认 /srv/tftpboot 下的引导文件确实存在。
5. Linux 里的显卡和无线网络:比想象中更有话题
很多人以为 Linux 对显卡的支持很差,其实这几年已经有了质的改善,尤其是 NVIDIA 驱动。不过自己折腾的时候还是有几个高频问题,特别是最近看到有人搜“三个 GPU 同时测试”,我估计是在跑深度学习或者多卡推理。
多卡环境的第一个坑是驱动安装之后,nvidia-smi 只能看到一张卡。这个大概率是 PCIe 链路或者电源管理的问题。建议先执行 lspci | grep -i nvidia 确认系统是否识别到了所有 GPU,如果这里都看不到某张卡,那大概率是硬件层面的插槽或供电问题。如果能识别但 nvidia-smi 不显示,可以考虑检查是否启用了显存持久化模式,nvidia-smi -pm 1。
跑多卡测试的时候,我习惯用 CUDA_VISIBLE_DEVICES 这样一个环境变量来控制任务分配到哪几张卡。比如我要测试 GPU 0 和 GPU 2 的算力情况,就用 CUDA_VISIBLE_DEVICES=0,2 python test.py。比这更细的排查,可以用 nvidia-smi dmon 实时看每张卡的使用率、显存读写和温度,比盯着 nvidia-smi 刷新效率高很多。
再来说 Linux 下无线网络的测试。如果是在电脑上装完 Linux 发现 WiFi 搜不到信号,第一步要先确认是不是网卡驱动没加载。常见的排查命令是 rfkill list,有时候是硬件开关被系统锁了,软锁定状态下运行 rfkill unblock wifi 就能恢复。如果是驱动问题,lspci -nnk 能看到有没有内核驱动被绑定。比如某些 Realtek 网卡默认没有打上 rtl8xxxu 之类的驱动,需要额外安装 dkms 版本。
判断无线信号质量的时候,命令行工具 iw 比图形界面的网络管理工具更直接。比如用 iw dev wlan0 link 查看当前连接速率,用 iw dev wlan0 scan 扫描周边热点。针对无线网络的长期稳定性测试,可以用 ping 加丢包统计来做:
bash复制ping -i 0.2 -c 1000 192.168.1.1 | tail -n 2
如果丢包率明显,再结合 ifconfig wlan0 查看 RX/TX 的 error 和 dropped 计数,可以大概判断是信号弱还是驱动问题。
6. 桌面 Linux 的日常:输入法、国产软件和那些奇奇怪怪的小需求
用 Linux 当主力桌面的人虽然不算多,但一旦把环境调顺了,日常办公和开发都很流畅。最容易让人觉得麻烦的是输入法。很多人装完输入法没反应,比如装好搜狗输入法之后在输入框里只能打英文,基本都是 fcitx 的输入法框架没设置好。Linux 桌面输入法的工作方式是:输入法框架启动,接管键盘事件,再往应用窗口里提交文本。如果环境变量没设置,应用就不会把键盘事件交给输入法框架。
建议安装完搜狗输入法之后,先确认 /etc/profile 或 ~/.xprofile 里有没有:
bash复制export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx
设置完重新登录,再执行 fcitx-status 确认框架运行。如果还是不行,检查一下是不是缺少 fcitx-configtool 之类的配置工具,搜狗输入法的语言包和主体是否都装齐了。千万别小看环境变量这步,很多人就是卡在这一行没写,结果框架装了几遍都没反应。
国产软件在 Linux 上的适配也越来越多。比如希沃白板有 Linux 版,企业微信也有 Linux 客户端,豆包也出了 Linux 版本。这些软件大多是 Electron 或者 Qt 封装,安装的时候要注意依赖库是否完整。跑不起来的时候,最常见的报错是缺 libgtk 或 libnss 相关的 so 文件。在终端里直接启动程序会打印出具体的缺少库名称,按提示装上就能解决。解决这类问题不需要很高的技术门槛,关键是敢看出错日志。
如果需要在 Linux 上跑一些安卓应用或者容器化应用,建议先把 Docker 环境整明白。注意 Docker 在国内环境下拉镜像有时不稳定,可以考虑配置国内镜像源。关于国内镜像源怎么配置,我一般会参考 Docker 官方文档确认地址格式,然后把 /etc/docker/daemon.json 里的 registry-mirrors 指向上游提供方。不要随意信任第三方博客里不明来源的镜像地址,以防安全风险。另外,配置完要重启 docker 服务并运行 docker info 查看 Registry Mirrors 是否生效。
7. 嵌入式 Linux 和内核日志:从启动报错到数据流走读
嵌入式 Linux 和普通桌面/服务器 Linux 的差别很大,核心在于交叉编译工具链、内核裁剪和根文件系统构建。很多人第一次接触嵌入式 Linux 项目,都是先拿到一块开发板,烧录系统之后发现启动日志里一堆看不懂的报错。其中一个被反复问到的报错是 respawn getty 相关的消息,比如 "respawn: "/sbin/getty" respawning too fast: disabled for 5 minutes"。
这个报错看起来吓人,实际上就是 getty 这个启动终端登录进程一直启动失败,导致 init 系统限制了它的重启频率。常见原因有三个:一是 /dev/console 或者 ttyS0 等串口设备节点不存在;二是 systemd 的 serial-getty 服务和实际串口号对不上;三是权限问题导致 getty 无法访问那个终端设备。排查路径是先看 dmesg | grep tty,确认内核实际注册了哪些串口设备,然后对照 systemctl status serial-getty@ttyS0 的状态再修正。
嵌入式调试还有一个绕不开的地方是内核日志。比如有人搜过 "linux tcp协议栈数据流走读",这种文章一般是研究 TCP 收发路径的源码走读,重点会落在 sk_buff 的分配和释放、协议栈各层之间的函数调用关系、以及 softirq 上下文的处理时机。看这类源码前最好先打好基础,至少理解 netfilter 的钩子点、NAPI 收包机制和 TCP 拥塞控制的大致流程,否则很容易迷失在函数指针的调用关系里。
还有一类跟内核相关的报错经常出现在 PCIe 设备上,搜出来的话大概是 "linux 内核无法给 pcie 桥接器分配足够的内存映射空间" 这种。这个问题的本质是 PCIe 桥的 MMIO 地址空间不够,常见于 BIOS 对 PCIe 窗口分配不合理的机器。简单粗暴的办法是在内核启动参数里加 pci=realloc 或者 pci=assign-busses,让内核重新分配总线资源和 MMIO 窗口。如果机器有多个 PCIe 插槽,把设备换一个不同的插槽有时也能绕开原先的地址分配冲突。
8. 大流量和并发:Linux 性能排查的杂货铺
日常工作中,除了功能性的部署和排错,还有很大一部分是性能问题排查。比如某台机器 CPU 使用率飙高,但不清楚是哪个进程或哪个内核线程导致的。这时候 top 看整体,ps 看进程,但如果要定位到线程级别,就需要 top -H -p PID 查看线程列表。如果需要进一步确认是不是内核态消耗过高,可以执行 vmstat 1 看 sy 列和 us 列的比例,如果是 sy 持续偏高,说明系统调用或上下文切换太过频繁,方向就得往锁竞争或者中断处理上找。
定位多核 CPU 负载不均衡的问题时,mpstat -P ALL 1 能看到每一颗逻辑核的使用情况。如果某个核特别忙,其他核很闲,可以考虑检查是不是有中断绑核、irqbalance 服务是否正常,或者某些单线程应用确实被调度到了一个固定核上。
内存层面的排查也非常重要。free -h 只是第一眼,真正要看的是 /proc/meminfo 里 MemAvailable 的值,因为它考虑到了 page cache 的可回收性。很多人看到 used 很高就以为内存不够,其实在 Linux 下大量使用 page cache 是正常现象。判断是否真的内存不足,要看是否频繁触发 swap 以及 vmstat 里的 si/so 是否不为零。如果 si/so 频繁跳动,说明内存真的告急,需要排查是哪个应用在吃内存。
如果遇到的是网络性能问题,除了用 iftop 看流量,还可以用 ethtool -S 检查网卡是否有 rx_dropped 或 tx_dropped。对于 TCP 重传问题,netstat -s | grep -i retrans 能看到重传统计。这些命令不需要很高级的技巧,但组合起来就能把问题大致定位到 CPU、内存、网络、磁盘四个层面里,再进一步用 strace、perf 或者 tcpdump 深入分析。
以我自己的经验来说,Linux 性能排查的核心不是某一个花哨工具,而是先明确瓶颈在哪一层,再选对应的工具去验证。这个过程绕不开多次尝试和验证,但每次排查完,你对操作系统的理解都会更深一层。
9. 面试和跳槽:Linux 知识怎么从会用变成懂原理
这几年转行和应届生越来越多,Linux 面试题的热度一直很高。常见的面试题翻来覆去就是文件权限、硬链接与软链接、进程与线程的区别、如何查看端口占用、如何杀掉僵尸进程。但面试官真正想考察的往往不是你会不会敲命令,而是碰到线上问题时的排查思路。
比如让你解释硬链接和软链接的区别,很多人只答一个是原文件路径的另一个入口,一个指向路径。但展开说的时候,要提到 inode 的概念,硬链接共享同一个 inode,软链接是独立 inode 存目标路径,目标被删除之后软链接会失效,硬链接不受影响,但硬链接不能跨文件系统,也不能给目录创建硬链接。这种深度是从实际使用中积累出来的。
我见过一个高频题:如何查看某个端口被哪个进程占用。最简单的答案是 netstat -tlnp | grep 端口号 或 ss -tlnp | grep 端口号。但如果能补充说明 netstat 在大量连接时性能不如 ss,以及 ss 是从 /proc 直接读取信息所以更快,再加上 lsof -i:端口 的替代方案,就会显得更有经验。
还有一类很典型的问题是 Linux 启动流程。从 BIOS/UEFI 到引导加载程序 GRUB,再加载内核,然后由 systemd 作为 1 号进程启动各项服务。理解启动流程的价值在于出问题时你知道在哪个阶段找原因,比如内核 panic 一般发生在挂载根文件系统之前,服务起不来则多半发生在 systemd 阶段。
如果要系统学习的话,我的建议是两条线并行:一条是命令和配置的熟练度,通过自己搭虚拟机、服务器来练,比如配置 Nginx 和静态 IP,处理网络故障;另一条是内核原理的阅读,从进程管理、内存管理、文件系统三个主线入手。嵌入式 Linux 项目、内核协议栈的源码走读这些进阶内容,都是建立在基础之上的。面试前如果能把常规面试题结合自己的经历写一遍,印象会非常深刻。
Linux 的价值在于你花时间折腾它,它就会回报你能掌控一切的感觉。从命令行提示符到系统深处,每一层都有值得琢磨的东西。写这篇笔记的时候我也在重新审视自己踩过的坑,希望能给你一些可以少走弯路的参考。
