1. 为什么“Linux基础”这四个字,坑了最多半路出家的开发者
刚入行那几年,我最怕别人问我“你Linux基础怎么样”。不是不会,是不知道从哪说起。说会吧,ls、cd、cp、mv敲得飞起,服务器上跑个脚本也没问题;说不会吧,一旦遇到权限报错、磁盘满了、进程杀不掉、服务起不来,立马抓瞎。后来带过几批新人,我发现一个特别普遍的现象:大多数人学Linux的方式,是从“命令大全”开始的,而不是从“操作系统到底在干什么”开始的。
这个顺序一错,后面全是坑。你背了chmod 755,但不知道755到底代表什么、为什么目录和文件的权限语义不一样;你会用ps aux | grep xxx,但进程卡在D状态时你完全不知道该怎么办;你装过nginx,但systemctl和service的区别、journalctl怎么看日志,全靠现搜。这些都不是“命令记得牢不牢”的问题,而是对Linux这套系统的运行模型没有建立基本认知。
所以这篇内容,我不打算给你列一张命令清单——那种东西网上一搜几万条。我想做的是,把“Linux基础”这个听起来特别泛的标题,拆成几个真正决定你能不能独立排障、独立部署、独立扛住一台服务器的核心模块。适合谁看?如果你是刚转行做后端、运维、测试,或者平时写代码但一碰服务器就发怵,那这篇就是写给你的。如果你已经能熟练排查Too many open files、能看懂top里的wa指标、能自己写systemd单元文件,那这篇可以当复习,重点看我在每个模块里塞的实操心得和踩坑记录。
我先把结论放前面:Linux基础的核心不是命令,而是四件事——文件系统与权限模型、进程与资源观、用户与权限边界、服务与日志体系。把这四块吃透,90%的日常问题你都能自己定位。下面我按这个逻辑,一块一块拆。
2. 文件系统与权限:别再把“777”当万能钥匙
2.1 一切皆文件,但“文件”和“目录”的权限含义完全不同
Linux里最核心的抽象就是“一切皆文件”。普通文件、目录、设备、管道、socket,在文件系统里都有对应的inode。但很多人栽跟头的地方在于:把文件和目录的rwx当成一回事。
对普通文件来说:
r:能读取文件内容w:能修改文件内容x:能执行这个文件(比如脚本、二进制)
对目录来说,语义完全变了:
r:能列出目录里有哪些文件(ls)w:能在目录里创建、删除、重命名文件x:能进入这个目录(cd),能访问目录内文件的元信息
这里有个特别经典的坑:一个目录权限是rw-(没有x),你ls能看到文件名,但cd不进去,也读不了里面文件的内容。反过来,目录只有x没有r,你cd能进去,但ls列不出来——只要你知道文件名,照样能访问。这个特性在共享目录场景里经常被用来做“可进入但不可枚举”的权限控制。
我见过太多人一遇到权限报错就chmod -R 777,这是最危险的习惯。777意味着任何用户都能改你的文件、删你的目录,线上环境这么干等于把门拆了。正确的做法是:先搞清楚“谁需要什么权限”,再用最小权限去配。
2.2 数字权限的速算逻辑,以及 umask 到底在干什么
chmod 755这个数字怎么来的?把rwx看成三位二进制:
| 权限 | 二进制 | 数字 |
|---|---|---|
| r-- | 100 | 4 |
| -w- | 010 | 2 |
| --x | 001 | 1 |
| rwx | 111 | 7 |
| r-x | 101 | 5 |
| rw- | 110 | 6 |
所以755= 所有者rwx,组r-x,其他人r-x。644= 所有者rw-,组r--,其他人r--。这是文件最常用的两组值:可执行文件/目录用755,普通数据文件用644。
但真正让新人困惑的是umask。你新建一个文件,默认权限不是777,而是666减去umask;新建目录是777减去umask。默认umask通常是022,所以:
- 新文件:
666 - 022 = 644 - 新目录:
777 - 022 = 755
注意这里是“按位清除”,不是数学减法。umask的本质是“默认要抹掉哪些权限位”。如果你把umask设成027,新文件就是640,新目录就是750,同组可读、其他人完全没权限。团队协作环境里,把umask统一设成027是个很实用的习惯,能避免文件默认对所有人开放。
提示:
umask是“屏蔽位”,不是“授予位”。你设umask 022,不代表文件一定有755,只是说新建时不会带上被屏蔽的位。理解这一点,就不会被“为什么我设了umask文件权限还是不对”绕进去。
2.3 特殊权限位:SUID、SGID、Sticky Bit 的真实用途
除了rwx,还有三个特殊位,很多人只在面试题里见过,实际排障时却认不出来。
SUID(Set User ID):作用在可执行文件上,任何人执行它时,都以文件所有者的身份运行。最典型的就是passwd命令——普通用户能改自己的密码,就是因为passwd带了 SUID,执行时临时获得 root 权限去写/etc/shadow。用ls -l看会显示所有者的x位变成s,比如-rwsr-xr-x。
SGID(Set Group ID):作用在目录上时,目录里新建的文件会继承目录的所属组,而不是创建者的主组。这个在共享项目目录里特别有用——大家往同一个目录里放文件,文件自动归属项目组,省得每次手动chgrp。
Sticky Bit:作用在目录上,目录里的文件只有所有者本人(或 root)才能删除。最典型的就是/tmp,所有人可写,但你不能删别人的文件。显示为其他人x位变成t,比如drwxrwxrwt。
这三个位用数字表示分别是4000、2000、1000。比如chmod 1777 /tmp就是给/tmp加 Sticky Bit。实际工作中,SGID 目录和 Sticky Bit 目录是最值得掌握的两个,前者解决协作归属问题,后者解决公共目录误删问题。
2.4 硬链接与软链接:删文件时到底发生了什么
理解链接,是理解 Linux 文件系统“引用计数”机制的最好入口。
硬链接:多个文件名指向同一个 inode。删掉其中一个文件名,inode 的链接计数减一,只要计数不为零,文件数据就还在。硬链接不能跨文件系统,也不能给目录建硬链接(防止循环引用)。
软链接(符号链接):一个独立的文件,内容是另一个文件的路径。删掉源文件,软链接就变成“断链”,访问会报No such file or directory。
这里有个实操中特别容易踩的坑:用rm -rf删软链接目录时,如果路径末尾带了斜杠,行为可能和你预期不一样。比如ln -s /data/app /opt/app,然后rm -rf /opt/app/,某些情况下会顺着链接把/data/app里的内容删掉。稳妥的做法是删链接时不要带尾斜杠,或者用rm /opt/app明确删链接本身。
另一个经验:备份或迁移时,优先用tar而不是cp -r。cp -r默认会跟随软链接,把链接指向的真实内容复制过去,可能造成数据膨胀或循环;tar默认保留链接属性,tar czf backup.tar.gz dir/打包再解包,链接关系原样保留。这个细节在部署环境迁移时能省掉大量麻烦。
3. 进程与资源:看懂 top 只是起点,会读 /proc 才算入门
3.1 进程的“父子关系”和孤儿、僵尸进程
Linux 里进程不是孤立的,每个进程都有父进程(除了 PID 1)。你用ps -ef看到的PPID就是父进程 ID。这个父子关系决定了信号传递、资源回收、终端控制等一堆行为。
僵尸进程(Zombie):子进程已经结束,但父进程还没调用wait()回收它的退出状态,于是这个进程的 PCB 还留在进程表里,状态显示为Z。僵尸进程本身不占 CPU 和内存(除了一个进程表项),但数量多了会耗尽 PID。解决办法不是杀僵尸(它已经死了,杀不掉),而是找到并处理它的父进程——父进程正常退出或被重启后,僵尸会被 PID 1 收养并回收。
孤儿进程:父进程先于子进程结束,子进程被 PID 1 收养。这个通常不是问题,反而是正常机制。
排查僵尸进程的完整链路:
# 1. 找出所有僵尸进程 ps aux | awk '$8 ~ /^Z/ {print $2, $11}' # 2. 找到它们的父进程 ps -o ppid= -p <僵尸PID> # 3. 查看父进程在干什么 ps -fp <父PID> # 4. 如果父进程是业务进程,检查它的代码是否正确 wait 子进程 # 如果父进程已经异常,考虑重启父进程我遇到过一种情况:某个采集程序 fork 子进程执行外部命令,但没处理SIGCHLD,子进程结束后全变僵尸,跑几天后 PID 耗尽,新进程起不来。这种问题的根因在代码,不在系统,但如果你不懂僵尸的成因,就会一直在系统层面瞎找。
3.2 进程状态里的 D、R、S、T、Z 分别意味着什么
ps或top里的STAT列,是判断进程健康度的第一手信息:
| 状态 | 含义 | 常见场景 |
|---|---|---|
| R | Running/Runnable | 正在跑或在等 CPU |
| S | Interruptible Sleep | 等事件,可被信号唤醒,最常见 |
| D | Uninterruptible Sleep | 等 I/O,不可被信号打断,危险信号 |
| T | Stopped | 被SIGSTOP暂停或调试中 |
| Z | Zombie | 已结束未回收 |
重点说D状态。进程处于D时,kill -9都杀不掉,因为它卡在内核态的 I/O 等待里。常见原因是磁盘故障、NFS 挂载失联、硬件问题。你看到一堆D状态进程,第一反应应该是查dmesg和 I/O 负载,而不是反复kill。
# 查看 D 状态进程 ps -eo pid,stat,wchan,comm | awk '$2 ~ /D/' # 看内核日志有没有 I/O 错误 dmesg -T | tail -50 # 看 I/O 等待 iostat -x 1 5wchan列会告诉你进程卡在哪个内核函数上,这是定位D状态根因的关键线索。
3.3 内存指标:free 里的 available 才是你该看的
free -h的输出里,很多人只看free那一列,看到数字很小就慌。其实 Linux 会把空闲内存拿去做缓存(buff/cache),这是设计如此,不是内存泄漏。
真正该看的是available列——它表示“在不使用 swap 的前提下,还能给新进程用多少内存”。这个值才是判断内存是否紧张的依据。
free -h # total used free shared buff/cache available # Mem: 15Gi 6.2Gi 1.1Gi 320Mi 8.4Gi 8.9Gi上面这个例子,free只有 1.1G,但available有 8.9G,说明内存很健康。反过来,如果available很低、swap使用量在涨,那才是真紧张。
还有一个坑:OOM Killer 杀进程时,你未必能在应用日志里看到原因。应用突然消失,日志没报错,很可能是被内核 OOM 杀了。查证方法:
dmesg -T | grep -i "out of memory" journalctl -k | grep -i oom如果确认是 OOM,要么加内存,要么给关键进程设oom_score_adj降低被杀优先级,要么用 cgroup 限制其他进程的内存。
3.4 用 /proc 直接读进程的实时状态
/proc是内核暴露给用户态的窗口,每个进程在/proc/<pid>/下都有对应目录。会读/proc,你就不依赖任何工具也能排障。
几个最常用的文件:
/proc/<pid>/status:进程状态、内存、UID、线程数/proc/<pid>/cmdline:启动命令(ps读的就是它)/proc/<pid>/fd/:打开的文件描述符,排查“文件被删但空间没释放”就靠它/proc/<pid>/limits:进程的资源限制,排查Too many open files必看/proc/meminfo:系统内存详情/proc/loadavg:负载
举个实战例子:磁盘满了,但du找不到大文件。这通常是因为某个进程还持有已删除文件的句柄,空间没释放。排查方法:
# 找出所有 deleted 状态但仍被占用的文件 lsof +L1 # 或者直接看 /proc ls -l /proc/*/fd/ 2>/dev/null | grep deleted找到对应进程后,重启它或者让它重新打开文件,空间才会真正释放。这个坑在日志切割场景里极其常见——你rm了旧日志,但写日志的进程没重启,磁盘照样满。
4. 用户、组与提权:sudo 不是万能,su 也不是
4.1 用户和组的本质:UID/GID 才是内核认的东西
Linux 内核不认用户名,只认 UID 和 GID。用户名只是/etc/passwd里给人看的映射。理解这一点,很多“诡异”现象就解释得通了。
比如你删了一个用户,但他之前创建的文件还在,ls -l显示的是一个数字 UID 而不是用户名——因为/etc/passwd里已经没有这个 UID 的映射了。这时候文件的所有权其实还在,只是“名字”丢了。
/etc/passwd存用户基本信息,/etc/shadow存密码哈希(只有 root 能读),/etc/group存组信息。这三个文件构成了本地用户体系。
4.2 su 和 sudo 的区别,以及为什么生产环境更推荐 sudo
su是切换用户,需要目标用户的密码(root 切普通用户不需要)。sudo是以另一个用户身份执行命令,需要的是当前用户的密码,且权限由/etc/sudoers控制。
生产环境更推荐sudo,原因有三:
- 可审计:
sudo的每次使用都会记录日志,谁在什么时候执行了什么命令,清清楚楚。su之后的操作归属就模糊了。 - 最小权限:
sudo可以精细控制某个用户只能执行特定命令,比如只允许重启某个服务,而不是给完整 root。 - 不用共享密码:
su到 root 意味着多人知道 root 密码,这是安全大忌。
配置sudo用visudo命令,不要直接编辑/etc/sudoers——visudo会做语法检查,防止你写错导致所有人都用不了 sudo。
一个实用的最小权限配置示例:
# 允许 deploy 用户无需密码重启 app 服务 deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart app.service # 允许 ops 组查看日志 %ops ALL=(ALL) /bin/journalctl -u app.service注意:
sudo规则里命令要写绝对路径,否则可能被同名的恶意程序绕过。这是很多人配 sudo 时忽略的安全细节。
4.3 文件属主、属组与“有效用户”的概念
进程运行时,有“真实用户 ID(RUID)”和“有效用户 ID(EUID)”两个概念。普通情况下两者相同,但 SUID 程序执行时,EUID 会变成文件所有者。内核做权限检查时,看的是 EUID。
这就解释了为什么passwd能让普通用户改密码——它的 EUID 是 root。也解释了为什么排查权限问题时,光看“我是谁”不够,还要看进程的 EUID。
# 查看当前 shell 的真实和有效用户 id # 查看某个进程的 EUID grep -E "Uid|Gid" /proc/<pid>/status4.4 实操心得:权限问题的排查顺序
遇到权限报错,我一般按这个顺序查,基本不会漏:
- 确认操作者身份:
id,看当前 UID/GID 和所属组。 - 确认目标文件权限:
ls -l,看 owner、group、mode。 - 确认路径上每一级目录的
x权限:这是最容易被忽略的。你要访问/a/b/c/file,/a、/a/b、/a/b/c每一级都要有x权限,缺一级都进不去。 - 确认父目录的
w权限:如果要创建或删除文件,父目录必须有w。 - 确认特殊权限位和 ACL:
getfacl看有没有额外的 ACL 规则。 - 确认 SELinux/AppArmor:如果前面都正常还报错,查
getenforce和ausearch。
这个顺序能覆盖 95% 的权限问题。剩下 5% 通常是挂载选项(比如noexec、nosuid)导致的,用mount | grep <挂载点>确认。
5. 服务、日志与开机自启:systemd 时代的必备技能
5.1 systemd 单元文件的结构与最小可用模板
现在主流发行版都用 systemd 管理服务。写一个能用的 unit 文件,是每个后端和运维的基本功。最小可用模板:
[Unit] Description=My Application After=network.target [Service] Type=simple User=appuser WorkingDirectory=/opt/app ExecStart=/opt/app/bin/start.sh Restart=on-failure RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target几个关键点解释:
Type=simple适合前台运行不退出的进程。如果是 fork 后父进程退出的老式程序,用Type=forking。Restart=on-failure让服务异常退出时自动重启,但正常退出(exit 0)不重启。生产环境强烈建议配上。RestartSec=5避免疯狂重启打爆日志。User=指定运行用户,别用 root 跑业务。WantedBy=multi-user.target决定systemctl enable时在哪个 target 下创建启动链接。
写完放到/etc/systemd/system/下,然后:
systemctl daemon-reload # 重载配置 systemctl enable myapp # 开机自启 systemctl start myapp # 启动 systemctl status myapp # 看状态提示:改完 unit 文件一定要
daemon-reload,否则 systemd 用的还是旧配置。这个坑我踩过不止一次,改了配置重启服务没生效,排查半天才发现忘了 reload。
5.2 journalctl 的实用查询姿势
systemd 的日志统一走 journal,journalctl是核心工具。几个高频用法:
# 看某个服务的日志 journalctl -u myapp # 实时跟踪 journalctl -u myapp -f # 看最近 100 行 journalctl -u myapp -n 100 # 按时间过滤 journalctl -u myapp --since "2024-01-01 10:00" --until "2024-01-01 11:00" # 只看错误级别 journalctl -u myapp -p err # 看本次开机以来的日志 journalctl -b # 看上次开机的日志(排查重启原因神器) journalctl -b -1journalctl -b -1这个用法特别值得记住。服务器意外重启后,你想知道重启前发生了什么,就看上一次 boot 的日志。如果日志没持久化(默认可能存内存),需要先在/etc/systemd/journald.conf里设Storage=persistent,日志才会写到/var/log/journal/。
5.3 日志轮转:别让日志把磁盘撑爆
日志不轮转,迟早撑爆磁盘。systemd 的 journal 有自己的轮转配置,在/etc/systemd/journald.conf:
[Journal] Storage=persistent SystemMaxUse=2G SystemMaxFileSize=200M MaxRetentionSec=2week普通应用日志用logrotate管理,配置在/etc/logrotate.d/下。一个典型配置:
/var/log/myapp/*.log { daily rotate 14 compress delaycompress missingok notifempty create 0640 appuser appuser sharedscripts postrotate systemctl reload myapp > /dev/null 2>&1 || true endscript }这里的关键是postrotate里的 reload。很多程序不会自动重新打开日志文件,你轮转后它还在往旧文件句柄写,导致新日志文件是空的、旧文件被删了空间还不释放。postrotate里让程序重新打开日志,才能真正完成轮转。这个细节是日志轮转最常见的坑。
5.4 开机自启的几种方式与排查
除了 systemd,还有几种自启方式,排查“为什么这个服务开机没起来”时要都查一遍:
| 方式 | 位置 | 排查命令 |
|---|---|---|
| systemd | /etc/systemd/system/ | systemctl is-enabled xxx |
| rc.local | /etc/rc.local | 看文件是否有执行权限 |
| cron @reboot | crontab -l | crontab -l | grep reboot |
| 用户级 systemd | ~/.config/systemd/user/ | systemctl --user |
排查顺序:先systemctl status看服务本身,再journalctl -b看启动过程有没有报错,最后确认依赖的服务(比如数据库)是不是还没起来。服务启动顺序问题是自启失败的高频原因——你的应用依赖网络,但After=network.target只保证网络栈初始化,不保证网络真的通了。需要更精确的依赖时,用After=network-online.target并配合Wants=network-online.target。
6. 把这些基础串起来:一次真实的排障复盘
前面讲的都是模块,但真实问题往往是跨模块的。我拿一个自己遇到过的案例串一遍,你能看到这些基础知识是怎么协同工作的。
现象:某台服务器上的应用响应越来越慢,最后完全无响应,SSH 也连不上,只能重启。
第一步,看负载和进程。重启前如果能进系统,top看到load average飙到 200 多,大量进程处于D状态。这说明不是 CPU 问题,是 I/O 卡死。
第二步,看 I/O 和内核日志。iostat -x 1显示某块盘%util100%,await极高。dmesg里有大量 I/O error 和task blocked for more than 120 seconds。基本确定是磁盘故障。
第三步,看是什么在疯狂读写。iotop或pidstat -d定位到是应用日志写入。但日志为什么会写这么多?查应用日志发现有个错误在循环刷屏,每秒几千条。
第四步,看磁盘为什么扛不住。df -h发现磁盘使用率 100%。但du找不到对应的大文件——这就是前面说的“文件被删但句柄未释放”。lsof +L1找到是应用进程持有已删除的旧日志文件,几个 G 的空间没释放。
第五步,根因:日志轮转配置了,但postrotate里没有让应用重新打开日志文件,导致轮转后应用继续写旧句柄,旧文件被删但空间不释放,磁盘逐渐满;磁盘满后应用写日志失败,进入错误循环,疯狂重试,I/O 被打满,最终系统卡死。
修复:
- 紧急释放空间:重启应用,释放旧句柄。
- 修 logrotate 配置,加上
postrotatereload。 - 给应用日志加限流,避免错误刷屏。
- 加磁盘监控告警,使用率超 80% 就报警。
这个案例里,用到了进程状态、I/O 指标、/proc和lsof、日志轮转、systemd 服务管理——全是“Linux基础”。你看,基础不牢,遇到这种问题就只能重启了事,下次还会再犯。
7. 给不同阶段读者的学习路径建议
最后说点实在的。Linux 基础这东西,不同阶段的人该练的重点不一样。
刚入门:别急着背命令。先把文件系统层级(/etc、/var、/proc、/usr各放什么)、权限模型(rwx对文件和目录的区别)、进程基本概念(PID、父子、状态)这三块搞明白。命令用到再查,查多了自然记住。
能独立部署服务:重点练 systemd 单元文件编写、journalctl 日志排查、logrotate 配置、sudo 最小权限配置。这几样是生产环境的日常。
想往深走:去读/proc和/sys,理解 cgroup 和 namespace,这是容器技术的底层。再学点strace、perf、tcpdump,排障能力会上一个台阶。
我自己的习惯是,每遇到一个新问题,解决后一定把排查过程记下来——用了什么命令、看了什么指标、根因是什么、怎么修的。攒上一年,你就有了自己的排障手册,比任何教程都管用。Linux 这东西,看一百篇不如自己动手排一次障。找个虚拟机,故意把磁盘写满、把权限改乱、把服务配错,然后自己修回来,这种练习比看视频有效得多。