hello 我是逆境
凌晨两点十七分,逆境的手机响了。
监控群里只有一条冷冰冰的消息:
订单服务响应超时,主机负载持续升高。他连上服务器,先敲top,发现 Load Average 已经到了 12;又敲free -h,空闲内存只剩两百多兆。更糟的是,df -h显示磁盘使用率 100%,可du怎么也找不到那么大的文件。
如果这些命令只是背过,屏幕上的数字就像体检报告上的缩写,每个字都认识,连在一起却不知道病在哪儿。
Linux 面试也经常这样。面试官很少满足于“命令是什么”,他会继续问:
负载高就一定是 CPU 不够吗?
文件删了,磁盘空间为什么没回来?kill -9发出去了,进程怎么还在?
空闲内存很少,系统是不是快崩了?
这篇文章就从这次夜间告警讲起。我们把 Linux 想成一栋一直营业的大楼:内核是物业中控室,进程是租户,内存是房间,文件描述符是取件号,磁盘和网络则是两条最容易堵车的运输线。先看懂它怎么运转,再背每一题末尾的“面试回答”。
第一站:进入大楼,先认清 Linux 的结构
逆境通过 SSH 登录服务器。命令行提示符出现的那一刻,他已经在和 Shell 打交道了,但 Shell 并不是 Linux 内核。
1. Linux 系统由哪些部分组成?
可以先记住四层:
用户程序 ↓ Shell、系统工具和各种运行库 ↓ Linux 内核 ↓ CPU、内存、磁盘、网卡等硬件内核负责进程调度、内存管理、文件系统、网络协议栈和设备驱动。Shell 是命令解释器,常见的有 Bash、Zsh。ls、ps、top这些命令属于用户态程序。
所以,严格来说 Linux 指的是内核;日常说“Linux 系统”时,通常把内核、GNU 工具、运行库和其他软件都算在里面。
面试回答:Linux 系统主要由内核、Shell、文件系统和用户态程序组成。内核负责调度、内存、文件、网络和设备管理;Shell 负责解释用户命令;应用程序通过系统调用向内核申请服务。
2. 用户态和内核态有什么区别?
把内核想成物业中控室。普通租户不能随便拉电闸、改门禁,用户程序同样不能直接访问硬件或任意内存。
CPU 在用户态运行应用代码时,权限受到限制。程序需要读取文件、申请内存或发送网络数据,就通过系统调用进入内核态。处理完成后再回到用户态。
这层隔离能保护系统,却不是免费的。每次切换都要保存和恢复部分执行现场,所以高频、细碎的系统调用会带来开销。
面试回答:用户态权限较低,应用不能直接访问硬件和内核空间;内核态拥有较高权限,负责执行系统级操作。系统调用、中断和异常都可能让 CPU 从用户态进入内核态,处理完成后再返回。
3. 什么是系统调用?
逆境执行:
catapp.logcat自己不会从磁盘控制器里“掏”出数据。它会调用open()打开文件,调用read()读取内容,再调用write()把内容写到终端。
常见系统调用可以按用途记:
文件:open、read、write、close 进程:fork、exec、wait、exit 网络:socket、bind、listen、accept 内存:mmap、brkShell 命令和系统调用不是一回事。cp是命令,内部会使用open、read、write等系统调用完成复制。
面试回答:系统调用是用户程序进入内核、请求操作系统服务的接口。应用通过它完成文件读写、进程创建、网络通信和内存管理。命令属于用户程序,命令内部通常还要调用多个系统调用。
4. “Linux 一切皆文件”到底是什么意思?
普通文件当然是文件,目录也是文件。磁盘设备可能出现在/dev,进程信息能在/proc里读取,网络 Socket 也能用文件描述符操作。
这句话强调的是统一接口:很多资源都可以执行“打开、读取、写入、关闭”这套动作。它是一种设计思想,不是说所有资源都真的存放在磁盘文件里。
例如/proc/cpuinfo看起来像文件,内容却是内核在读取时动态提供的。
面试回答:“一切皆文件”指 Linux 将普通文件、目录、设备、管道和 Socket 等资源抽象成类似文件的接口,程序可以通过文件描述符以及读写操作统一管理它们。它是接口抽象,不代表所有资源都是磁盘文件。
5. 常见 Linux 目录分别放什么?
逆境排障时最常去的是/etc、/var/log和/proc。
/etc 系统与服务配置 /var 日志、缓存和经常变化的数据 /usr 大量用户程序、库和共享文件 /home 普通用户家目录 /root root 用户家目录 /tmp 临时文件 /dev 设备文件 /proc 进程与内核信息,属于虚拟文件系统 /sys 设备和内核对象信息 /run 本次启动以来的运行时数据不必死背整棵目录树。面试中能说清配置去/etc、日志常在/var/log、进程状态看/proc,已经抓住了最常用的部分。
面试回答:
/etc放配置,/var放日志等可变数据,/usr放程序和库,/home是普通用户目录,/dev提供设备文件,/proc和/sys提供内核、进程及设备的运行信息。
第二站:进程大厅,谁在工作,谁只剩一张登记表
逆境输入ps -ef,屏幕上滚过几百行进程。这里有正在工作的,也有睡觉等消息的,还有已经死亡却没有办完注销手续的。
6. 进程和线程有什么区别?
进程像一家公司,有自己的办公区和资源。线程像公司里的员工,共用办公室、文件柜和电话,但每个人都有自己的工作台。
同一进程中的线程通常共享:
- 虚拟地址空间
- 打开的文件描述符
- 全局变量和堆
每个线程又有独立的栈、寄存器和程序计数器。
进程隔离更好,一个进程崩溃通常不会直接破坏另一个进程的地址空间。线程之间通信方便,切换成本一般也更低,但共享数据时要处理锁、竞态条件和可见性问题。
面试回答:进程是资源分配和隔离的基本单位,线程是 CPU 调度的基本单位。同一进程的线程共享地址空间和文件描述符,但各自拥有栈、寄存器和程序计数器。线程通信方便、切换通常较轻,代价是共享数据容易产生并发问题。
7.fork()和exec()有什么区别?
Linux 创建新程序时常走两步:
父进程 ↓ fork 子进程 ↓ exec 新程序fork()复制出一个子进程。子进程有新的 PID,开始时看起来和父进程很像。exec()则把当前进程的代码、数据和栈替换成另一个程序。
exec()成功后不会返回到旧程序继续执行。它通常也不会创建新 PID,变的是进程里运行的内容。
面试回答:
fork()创建子进程,父子进程从调用位置继续执行;exec()用新程序替换当前进程的地址空间。常见模式是先fork()创建子进程,再由子进程调用exec()运行目标程序。
8.fork()会立刻复制父进程的全部内存吗?
通常不会。
Linux 使用写时复制,英文是 Copy-on-Write。刚执行完fork()时,父子进程的虚拟地址会映射到相同的物理页面,并把页面暂时设为只读。谁尝试修改,内核才为谁复制对应的页面。
这像两个人共用一份只读资料。没人改时不必复印;有人要在某一页做笔记,只复印那一页。
写时复制节省了时间和内存,尤其适合子进程马上调用exec()的场景。
面试回答:
fork()通常采用写时复制,不会立即复制全部物理内存。父子进程起初共享物理页,当一方写入时触发缺页异常,内核再复制对应页面。
9. Linux 进程有哪些常见状态?
执行ps或top时,经常能看到这些字母:
R 正在运行,或已经准备好等待 CPU S 可中断睡眠,通常在等待事件 D 不可中断睡眠,常见于等待 I/O T 被暂停或正在被调试 Z 僵尸进程最容易误解的是R和D。
R不只表示正在占用 CPU,也包括已经排队等待 CPU。D状态下的进程通常正在等内核完成某项 I/O,为了避免操作进行到一半被打断,暂时不会响应普通信号。
面试回答:常见状态有运行或就绪的
R、可中断睡眠的S、不可中断睡眠的D、暂停的T和僵尸状态Z。其中D常与磁盘或网络存储 I/O 等内核等待有关。
10. 什么是僵尸进程?
子进程退出时,会留下 PID、退出码和少量统计信息,等父进程调用wait()或waitpid()来领取。
如果父进程一直不领,子进程已经不执行代码,也不占正常的用户内存,却仍占着进程表项。它就是僵尸进程。
少量、短暂的僵尸进程不一定有问题。大量长期存在的僵尸进程说明父进程没有正确回收子进程,最终可能耗尽 PID 或进程表资源。
面试回答:僵尸进程是已经退出、但父进程尚未调用
wait()回收退出状态的子进程。它不再运行,却保留进程表项。解决时应修复父进程的子进程回收逻辑,而不是只盯着僵尸进程本身。
11. 什么是孤儿进程?它和僵尸进程一样吗?
不一样。
孤儿进程仍在运行,只是父进程先退出了。它通常会被 PID 1 或配置为 subreaper 的进程接管,之后照常运行并被回收。
僵尸进程已经结束,只剩退出信息没人领取。一个是“孩子还活着,家长先走了”,另一个是“孩子已经退场,家长没签收结果”。
面试回答:孤儿进程是父进程已经退出、子进程仍在运行的进程,通常会被 PID 1 或 subreaper 接管;僵尸进程则已经退出,只是退出状态尚未被父进程回收。
12.kill命令是直接杀进程吗?
kill的本意是发送信号。
killPID# 默认发送 SIGTERM,也就是 15kill-9PID# 发送 SIGKILLkill-HUPPID# 发送 SIGHUPSIGTERM是一次“请你退出”的通知。程序可以捕获它,先停止接收请求、保存数据、关闭连接,再结束。
SIGKILL则由内核直接处理,进程不能捕获、忽略或执行清理逻辑。所以生产环境通常先发SIGTERM,超时后再考虑SIGKILL。
还有一个面试陷阱:处于D状态的进程即使收到了SIGKILL,也可能暂时退不掉。它要等不可中断的内核操作返回,才有机会处理退出。
面试回答:
kill是发送信号,默认发送可被处理的SIGTERM,让进程有机会优雅退出。kill -9发送不可捕获的SIGKILL,可能造成数据或资源来不及清理;若进程卡在D状态,它也不一定立刻消失。
13. 什么是上下文切换?为什么太多会变慢?
CPU 核心同一时刻只能执行有限数量的线程。它从任务 A 切到任务 B 时,要保存 A 的寄存器和执行位置,再恢复 B 的现场。
这段交接时间没有直接处理业务,还可能让 CPU 缓存失效。线程数量失控、锁竞争严重、频繁进行系统调用,都可能增加上下文切换。
常用观察命令:
vmstat1pidstat-w1vmstat中的cs可以观察每秒上下文切换次数,但“多少算高”没有统一数字,要和机器核数、负载及历史基线一起判断。
面试回答:上下文切换是 CPU 在任务之间切换时保存和恢复执行现场的过程。切换本身消耗 CPU,也可能破坏缓存局部性。线程过多、锁竞争和频繁系统调用都会让切换增多。
14. nice 值表示什么?数值越大优先级越高吗?
恰好相反。
普通进程的 nice 值通常在-20到19之间。数值越小,调度优先级越高;数值越大,表示这个进程越“客气”,愿意把 CPU 让给别人。
nice-n10commandrenice5-pPIDnice 值只影响普通调度策略下的相对权重,不等于实时调度优先级,也不能保证某个进程一定先运行。
面试回答:nice 值用于影响普通进程的调度权重,通常范围是
-20到19,值越小优先级越高。它表达的是相对倾向,不是严格的执行顺序保证。
15. 什么是守护进程?systemd 又是什么?
守护进程是在后台长期运行、提供服务的进程,例如 SSH 服务和定时任务服务。它们通常不依赖交互终端。
现代 Linux 发行版常用 systemd 作为 PID 1,负责系统启动、服务依赖、进程监管和日志配合。操作服务时经常用:
systemctl status nginx systemctl start nginx systemctl restart nginx systemctlenablenginx journalctl-unginxstart是现在启动,enable是设置开机自动启动,两者别混。
面试回答:守护进程是在后台长期运行并提供服务的进程。systemd 是常见的初始化和服务管理系统,通常作为 PID 1 管理系统启动、服务生命周期和依赖关系。
start控制当前状态,enable控制是否开机自启。
第三站:CPU 候诊室,负载 12 不等于 CPU 使用率 1200%
回到那条告警。服务器有 8 个 CPU 核心,Load Average 是:
12.40 10.83 7.16数字一路上涨,但top里的 CPU 还有不少空闲。逆境这才意识到,等待 CPU 的任务不是负载的全部。
16. Load Average 到底统计什么?
Load Average 是过去 1、5、15 分钟的平均活跃任务数。Linux 主要统计两类任务:
- 正在运行或等待 CPU 的
R状态任务 - 处于不可中断睡眠的
D状态任务
所以负载高有两种常见方向:CPU 队列太长,或者大量任务卡在 I/O。
它也不是“CPU 使用率的平均值”。负载是任务数量,CPU 使用率是 CPU 时间花在各类工作的比例。
面试回答:Load Average 表示过去 1、5、15 分钟内,处于运行、等待 CPU 和不可中断睡眠状态的平均任务数。它既可能反映 CPU 排队,也可能反映 I/O 阻塞,不能直接等同于 CPU 使用率。
17. Load Average 多高算高?
先看 CPU 核数。
在 8 核机器上,长期负载接近 8,说明平均每个核心大致有一个活跃任务;长期明显超过 8,说明任务正在排队。可这仍不是判决书。
如果负载 12,CPU 几乎跑满,优先找高 CPU 进程。如果负载 12,CPU 却很空闲,就去看D状态进程、磁盘延迟和网络存储。
短暂峰值也不一定值得惊慌。1 分钟负载很高、5 分钟和 15 分钟负载较低,可能只是刚出现的一阵流量。
面试回答:负载要结合 CPU 核数、持续时间和 CPU 使用率判断。长期高于核心数通常说明有排队;负载高但 CPU 不忙时,应重点检查
D状态任务和 I/O 等待。
18.top里的us、sy、id、wa分别是什么?
常见字段可以这样记:
us 用户态程序使用的 CPU sy 内核态使用的 CPU id CPU 空闲时间 wa CPU 等待 I/O 的时间 st 虚拟机被宿主机拿走的 CPU 时间us高,常见于业务计算、死循环、序列化或垃圾回收。sy高,要留意频繁系统调用、网络包处理和内核工作。wa高通常把视线引向磁盘或远程存储,但还要结合iostat验证。
面试回答:
us是用户态 CPU 时间,sy是内核态 CPU 时间,id是空闲时间,wa是等待 I/O 的时间,虚拟化环境中的st表示被宿主机占用的时间。不同字段偏高,对应的排查方向不同。
19. CPU 使用率很高,怎么找到原因?
逆境一般按三层往下找:
topps-eopid,ppid,cmd,%cpu--sort=-%cputop-H-pPID pidstat-pPID1先找高 CPU 进程,再看进程里的高 CPU 线程,最后结合程序的线程栈、性能剖析和日志判断是死循环、热点计算、锁竞争,还是频繁系统调用。
Java 程序中,常把 Linux 线程 ID 转成十六进制,再到线程栈里寻找对应的nid。只背这一步不够,面试时最好补一句:线程栈应该多抓几次,确认它持续卡在同一段代码,而不是碰巧采到一次。
面试回答:先用
top或ps找到高 CPU 进程,再用top -H -p PID找高 CPU 线程,最后结合线程栈、性能剖析和应用日志定位代码。还要区分用户态计算、内核态开销和上下文切换。
20. 负载很高,CPU 却不高,可能是什么原因?
最常见的是 I/O 堵塞。大量任务在等待磁盘、NFS 或其他内核 I/O,进入D状态,它们会被计入负载,却没有持续占用 CPU。
可以继续看:
ps-eostate,pid,ppid,cmd|grep'^D'iostat-x1pidstat-d1如果是数据库服务器,还要考虑存储延迟、慢查询和大量刷盘。
面试回答:高负载而 CPU 不高,通常说明有大量
D状态任务等待 I/O,也可能存在锁或资源等待。应结合进程状态、iostat、pidstat和存储情况继续定位,不能看到负载高就直接扩 CPU。
第四站:内存仓库,空房间少不代表住满了
free -h显示 free 很小。逆境的第一反应是内存不足,但旁边的 available 还剩好几个 GB。
Linux 不喜欢让内存闲着。暂时没人住的房间,会先拿来堆放最近用过的文件。新租户来了,这些房间大多能很快腾出来。
21. 什么是虚拟内存?
每个进程看到的都是一套独立的虚拟地址空间。程序使用虚拟地址,CPU 和内核再通过页表把它翻译成物理内存地址。
这样做有几个直接好处:
- 进程彼此隔离,不容易读写别人的内存
- 程序看到连续地址,底层物理页却可以分散
- 多个进程可以共享同一份只读代码或共享内存
- 暂时不用的页面可以换出,文件也可以按需映射
虚拟内存不是 Swap 的另一种叫法。Swap 只是虚拟内存机制可能使用的一块磁盘空间。
面试回答:虚拟内存为每个进程提供独立的虚拟地址空间,再通过页表映射到物理内存。它实现了进程隔离、灵活分配、共享页面和按需加载。Swap 只是其中可能用到的后备存储,不等于虚拟内存本身。
22. 什么是分页?为什么要按页管理内存?
Linux 把虚拟内存和物理内存切成固定大小的页,常见基础页大小是 4 KB,但具体值与架构和配置有关。
分页让内核不必寻找一整块连续物理内存,也方便设置每一页的读、写、执行权限。页表负责记录虚拟页和物理页之间的映射。
代价也很明显:页表本身要占空间,地址翻译要花时间,因此 CPU 会使用 TLB 缓存近期的地址映射。
面试回答:分页把虚拟地址空间和物理内存划分为固定大小的页,通过页表建立映射。它便于离散分配、权限控制和内存共享,但会带来页表空间和地址转换开销,CPU 通常用 TLB 加速转换。
23. 什么是缺页异常?缺页就一定要读磁盘吗?
程序访问某个虚拟页时,如果当前映射不满足访问要求,CPU 会触发缺页异常,把处理权交给内核。
缺页不一定读磁盘:
- Minor Page Fault:数据已经在内存中,可能只需建立页表映射。
- Major Page Fault:所需内容不在内存,需要从磁盘或其他后备存储读入。
Major Fault 的代价通常大得多。写时复制第一次发生写入时,也会通过缺页异常让内核复制页面。
面试回答:缺页异常发生在进程访问的虚拟页没有有效映射或权限不满足时。轻微缺页通常只需建立映射,不必读盘;严重缺页需要从磁盘加载数据。写时复制也会借助缺页异常完成页面复制。
24. 什么是 Page Cache?
Page Cache 是内核用内存缓存文件内容的机制。
第一次读取文件可能要访问磁盘,之后再读同一部分,往往可以直接从内存返回。普通文件写入时,数据也可能先进入 Page Cache,变成脏页,随后由内核异步刷盘。
这解释了两个现象:
- 第二次读文件常比第一次快。
write()成功通常表示数据交给了内核,不一定已经安全落盘。需要严格持久化语义时,要根据场景使用fsync()等机制。
面试回答:Page Cache 用内存缓存文件数据,减少磁盘访问。读操作可以命中缓存,写操作常先修改缓存页,再异步刷盘。因此写系统调用返回不一定代表数据已经持久化到物理磁盘。
25.free很小,为什么系统可能仍然健康?
看这条命令:
free-hfree只表示完全没有被使用的内存。Linux 会把空闲内存用于 Page Cache 等用途,需要时再回收,所以free小很常见。
更值得看的是available,它估算了在不发生明显 Swap 的情况下,系统还能提供给新程序的内存。判断内存压力时,还应结合 Swap 活动、回收速度和应用延迟。
面试回答:Linux 会主动使用空闲内存作为缓存,所以
free很小不等于内存不足。通常更关注available,再结合 Swap、页面换入换出、OOM 日志和应用表现判断真实压力。
26. 什么是 Swap?用了 Swap 就一定有问题吗?
Swap 是磁盘上的交换空间。物理内存紧张时,内核可以把一部分不活跃的匿名页换出去,为更活跃的数据腾位置。
少量使用过 Swap,不代表系统正在出故障。某些页面很久没再访问,即使之后内存宽松,也未必立刻换回。
真正危险的是持续、频繁地换入换出。磁盘比内存慢得多,系统可能把大量时间花在搬页面上,这叫内存抖动。
vmstat1可以关注si和so,它们反映 Swap 换入和换出活动。
面试回答:Swap 用磁盘保存暂时不活跃的内存页,可以缓解物理内存压力。Swap 有占用不一定异常,但持续大量换入换出会造成严重延迟,应结合
vmstat的si、so和业务表现判断。
27. 什么是 OOM Killer?
当内核已经很难满足新的内存分配,也无法通过回收缓存或换出页面解决时,可能启动 OOM Killer。
它会根据进程内存占用、可回收性、oom_score_adj等因素选择牺牲对象,终止某个或某些进程来保住系统。
排查时先找证据:
dmesg|grep-ioom journalctl-k|grep-ioom容器环境还要区分主机 OOM 和容器内存限制触发的 OOM。一个 Java 进程堆没有占满,也可能因为直接内存、线程栈或容器限制被杀。
面试回答:OOM Killer 是 Linux 在严重内存不足时的自救机制,会根据评分选择进程终止并释放内存。排查要查看内核日志,并区分主机内存不足、容器限制、堆内存和堆外内存等来源。
28. 内存一直上涨,怎么区分内存泄漏和缓存增长?
先看增长的是谁。
free-hps-eopid,cmd,rss,%mem--sort=-rss pmap-xPID如果 Page Cache 增长,而available仍充足,系统通常可以回收它。若某个进程的 RSS 长期上升、业务量回落后也不下降,就要继续检查堆、直接内存、线程数量和内存映射。
“内存没有立刻还给操作系统”也不一定是泄漏。语言运行时和内存分配器可能保留空闲区域以便复用。判断泄漏要看对象或分配是否失去控制地持续增长。
面试回答:先区分增长来自系统缓存还是进程常驻内存。Page Cache 通常可回收;进程 RSS 长期增长且负载回落后仍不稳定,才需要结合堆分析、线程、直接内存和内存映射继续判断。占用不下降不等于必然泄漏。
第五站:文件仓库,文件名只是门牌号
磁盘告警仍然没有解决。逆境先查容量,又查 inode,最后发现一个已经删除的日志文件仍被 Java 进程打开。
想看懂这个问题,得先把“文件名”和“文件本身”拆开。
29. inode 是什么?
inode 保存文件的元数据和数据块位置信息,例如:
- 文件类型和权限
- 所有者与所属组
- 文件大小和时间
- 链接计数
- 数据块位置
文件名不在 inode 里。目录保存“文件名到 inode 编号”的映射。
可以把 inode 看成房产档案,文件名则是门牌。一个档案可以有多个门牌,这就是硬链接能够存在的原因。
面试回答:inode 保存文件的元数据和数据块索引,文件名保存在目录项中,目录项负责把名字映射到 inode。文件系统查找文件时,先根据目录项找到 inode,再访问实际数据。
30. 磁盘明明还有容量,为什么创建不了文件?
可能是 inode 用完了。
文件系统创建每个文件都需要 inode。大量小文件会先耗尽 inode,即使数据区还剩几十 GB,也无法继续创建文件。
df-h# 看容量df-i# 看 inode缓存目录、会话文件和没有轮转的小日志碎片,都是常见来源。
面试回答:磁盘空间包含数据块和 inode 两类资源。大量小文件可能耗尽 inode,导致容量尚有剩余却无法创建新文件。可以用
df -h查看容量,用df -i查看 inode 使用情况。
31. 硬链接和软链接有什么区别?
硬链接是给同一个 inode 再起一个名字:
lnsourcehard_link删除原文件名,只是减少一次链接计数,另一个硬链接仍能访问数据。硬链接通常不能跨文件系统,也通常不允许普通用户为目录创建硬链接。
软链接则是一个独立文件,里面保存目标路径:
ln-ssourcesoft_link它可以跨文件系统,也可以指向目录;目标路径失效后,软链接就会变成“断链”。
面试回答:硬链接与原文件指向同一个 inode,删除其中一个名字不影响其他硬链接,通常不能跨文件系统;软链接有自己的 inode,内容是目标路径,可以跨文件系统和链接目录,但目标删除后会失效。
32.chmod 755是什么意思?
权限分为三组:
所有者 所属组 其他用户 rwx r-x r-x 7 5 5其中r=4、w=2、x=1,把同一组需要的权限相加即可。
对普通文件来说,r是读内容,w是修改内容,x是执行。目录稍有不同:
r:列出目录项w:创建、删除或重命名目录中的条目x:进入目录,并通过名字访问其中的条目
这也是为什么“文件不可写”不一定阻止删除文件。删除动作修改的是父目录的目录项,主要看父目录权限。
面试回答:
755表示所有者拥有读写执行权限,组用户和其他用户拥有读执行权限。目录的读权限控制列出内容,写权限控制增删目录项,执行权限控制进入和按名称访问。
33. SUID、SGID 和 Sticky Bit 是什么?
它们是三种特殊权限。
SUID 设置在可执行文件上时,进程执行期间可以使用文件所有者的有效身份。典型例子是普通用户修改密码时使用的passwd。
SGID 用在可执行文件上与组身份有关;用在目录上时,新文件通常继承目录的所属组,适合共享目录。
Sticky Bit 常见于/tmp。目录人人可写,但用户通常只能删除自己的文件,避免互相乱删。
面试回答:SUID 让可执行文件以文件所有者的有效身份运行;SGID 可以让程序使用文件所属组身份,设置在目录上时还能让新文件继承目录组;Sticky Bit 用于共享可写目录,限制用户删除他人的文件。
34. 什么是文件描述符?
进程打开文件、管道或 Socket 后,内核返回一个非负整数,这就是文件描述符。
0 标准输入 1 标准输出 2 标准错误文件描述符只是进程自己的索引。它会指向内核中的打开文件对象,后者再关联 inode、当前偏移量和访问模式等信息。
查看限制:
ulimit-ncat/proc/PID/limitsls/proc/PID/fd服务报Too many open files时,不能只把限制调大。还要检查连接或文件是否没有关闭。
面试回答:文件描述符是进程访问已打开文件、管道和 Socket 的整数句柄。每个进程有自己的描述符表,描述符再指向内核的打开文件对象。耗尽时既要检查上限,也要排查资源泄漏。
35.df和du为什么会统计不一致?
df从文件系统角度统计已经分配的数据块,du遍历目录,统计当前能通过目录项看到的文件。
如果一个大日志已经被删除,但进程仍然打开它,目录项虽然没了,文件数据还不能释放:
rm 删除文件名 ↓ 目录里已经看不见 ↓ 进程仍持有文件描述符 ↓ 数据块继续占用磁盘这时du找不到它,df却仍认为空间被占用。
lsof+L1可以查找链接计数为 0、却仍被进程打开的文件。
面试回答:
df按文件系统已分配块统计,du按目录中可见文件统计。文件被删除但仍由进程打开时,目录项已经消失,数据块却没有释放,因此会出现df高、du低。
36. 文件已经删除,怎样安全释放被占用的空间?
最稳妥的做法是让应用正常关闭文件描述符,例如重新加载日志、滚动日志或重启对应服务。
不要一看到大文件就粗暴处理。先确认占用进程和文件:
lsof+L1某些紧急场景会通过/proc/PID/fd/FD截断文件,但这可能影响应用的写入位置和日志行为,不能当成通用方案。
夜里那次故障中,逆境先确认服务有两个实例,再滚动重启持有旧日志的实例。磁盘空间随文件描述符关闭而释放,订单没有整体中断。
面试回答:删除文件只移除目录项,最后一个文件描述符关闭后数据块才会释放。应先用
lsof +L1找到占用进程,再通过日志重开、滚动重启或正常关闭文件的方式释放空间。
第六站:I/O 与网络通道,连接多不等于都在干活
磁盘空间恢复后,负载慢慢下降。但订单接口仍有一部分请求超时。逆境继续检查监听端口和连接状态。
37. 阻塞、非阻塞、同步、异步有什么区别?
这两组概念经常被混在一起。
阻塞与非阻塞,描述调用线程在结果暂时拿不到时怎么办:
- 阻塞:线程停下来等。
- 非阻塞:调用立即返回,线程可以先做别的事。
同步与异步,更关心结果由谁完成、怎样通知:
- 同步:调用方主动等待或轮询结果。
- 异步:任务完成后由系统通过回调、事件等方式通知。
非阻塞不等于异步。例如线程反复调用非阻塞read()轮询,仍然是调用方自己追着问结果。
面试回答:阻塞和非阻塞描述调用在结果未就绪时是否等待;同步和异步描述结果完成及通知方式。非阻塞调用可以立即返回,但如果调用方持续轮询,它仍不等于真正的异步处理。
38.select、poll、epoll有什么区别?
它们都用于同时等待多个文件描述符就绪。
select每次都要传入描述符集合,内核返回后,应用还要遍历寻找谁就绪;它通常还有集合大小限制。
poll使用数组,摆脱了select固定集合大小的常见限制,但寻找就绪描述符仍要线性扫描。
epoll在内核维护关注列表。应用注册事件后,等待时主要获取已经就绪的描述符,更适合“大量连接、少量活跃”的服务器场景。
不要把epoll背成“任何情况都更快”。连接数很少或全部一直活跃时,它的优势会缩小,实际性能还受业务模型和实现影响。
面试回答:
select和poll每次等待都需要传递或检查一批描述符,查找就绪事件通常是线性的;epoll在内核维护关注集合,只返回就绪事件,更适合大量连接、少量活跃的场景。
39. epoll 的 LT 和 ET 有什么区别?
LT 是水平触发。只要接收缓冲区里还有数据,每次等待时都可能继续提醒,像水杯没喝空就一直亮灯。
ET 是边缘触发。状态从“没数据”变为“有数据”时提醒一次,像只在水刚流进来时响铃。如果这次没读完,未必马上再提醒。
ET 通常配合非阻塞 I/O,并循环读到返回EAGAIN。否则剩余数据可能长时间得不到处理。
面试回答:LT 只要描述符仍处于就绪状态就会重复通知,编程简单;ET 通常只在状态变化时通知,需要配合非阻塞 I/O,一次处理到返回
EAGAIN。ET 减少重复通知,但更容易写错。
40. 什么是零拷贝?
传统文件发送可能经历这样的过程:
磁盘 ↓ 内核缓冲区 ↓ 用户缓冲区 ↓ Socket 内核缓冲区 ↓ 网卡中间的数据复制和用户态、内核态切换会消耗 CPU。零拷贝技术尽量减少这些不必要的复制或切换,例如 Linux 的sendfile()可以让文件数据在内核路径中直接送往 Socket。
“零拷贝”通常不是物理上一次复制都没有,而是避免数据在用户空间和内核空间之间来回搬运。具体复制次数与内核、网卡能力和实现有关。
面试回答:零拷贝通过
sendfile、mmap等机制减少用户空间与内核空间之间的数据复制和上下文切换,降低 CPU 开销。它通常表示减少不必要的复制,不一定是整个硬件链路绝对零复制。
41. 端口被占用,怎么查?
先看谁在监听:
ss-lntpss-lntp|grep:8080lsof-i:8080ss中:
l 只看监听 n 不解析名称,直接显示数字 t TCP p 显示进程找到 PID 后,再确认它是旧实例、正常依赖,还是意外启动的程序。不要条件反射地kill -9,否则很可能把真正提供服务的进程干掉。
面试回答:可以使用
ss -lntp或lsof -i :端口查找监听端口及对应进程。确认进程用途后,再决定停止旧实例、修改端口还是修复重复启动问题。
42. 进程还在,为什么服务访问不了?
“进程存在”只说明进程表里还有它,不代表服务已经能正常处理请求。
排查顺序可以沿着请求路径走:
进程是否存活 ↓ 端口是否监听 ↓ 监听在 127.0.0.1 还是 0.0.0.0 ↓ 本机访问是否成功 ↓ 防火墙、安全组、路由是否放行 ↓ 反向代理和负载均衡是否正常 ↓ 应用线程池、连接池和下游依赖是否耗尽常用命令:
ps-efss-lntpcurl-vhttp://127.0.0.1:8080/health journalctl-uservice-name如果端口存在但请求一直挂起,应用可能死锁、线程池耗尽、GC 停顿,或者卡在数据库等下游服务。
面试回答:应沿请求链路排查,先确认进程和监听端口,再检查监听地址、本机访问、防火墙、代理和负载均衡,最后查看线程池、连接池、日志及下游依赖。进程存活不等于服务可用。
43. TCP 常见连接状态有哪些?
面试中最常问的是这几个:
LISTEN 服务端正在监听 ESTABLISHED 连接已经建立 SYN-SENT 主动发起连接,等待回应 SYN-RECV 收到连接请求,等待最后一次握手 TIME-WAIT 主动关闭方等待旧报文过期 CLOSE-WAIT 被动关闭方收到 FIN,但本地应用还没关闭大量TIME-WAIT不一定是故障,它是 TCP 主动关闭方的正常状态,用于避免旧报文影响新连接,并保证最后的 ACK 有机会重传。
大量CLOSE-WAIT更值得检查,常见原因是对端已经关闭连接,本地程序却没有及时close()。
面试回答:
TIME-WAIT通常出现在主动关闭连接的一方,用来处理迟到报文和最后 ACK 重传;大量CLOSE-WAIT往往说明对端已经关闭,而本地应用没有及时释放连接,应检查程序的连接关闭逻辑。
第七站:把排障过程说成一条线
面试官问“服务器突然变慢怎么查”,最怕听到的是一串没有顺序的命令:
top、free、df、netstat、jstack、iostat……
命令都对,但像把工具箱倒在地上。更好的回答是先分类,再逐层定位。
44. Linux 服务器整体变慢,怎么排查?
逆境会先看四个方向:
uptime# 负载趋势top# CPU、内存、进程vmstat1# 运行队列、内存、换页、上下文切换free-h# 内存iostat-x1# 磁盘延迟和繁忙程度df-h# 磁盘容量df-i# inodess-s# 网络连接概况dmesg# 内核错误思路比命令更重要:
- 先确认影响范围,是单个接口、单个进程,还是整台机器。
- 判断瓶颈在 CPU、内存、磁盘还是网络。
- 从系统指标缩小到具体进程和线程。
- 对照应用日志、发布时间和流量变化,确认根因。
比如 CPU 高就找高 CPU 线程;负载高但 CPU 空闲就看 I/O;Swap 持续进出就查内存压力;只有某个接口慢,则更可能是应用锁、数据库或下游调用。
面试回答:我会先确认影响范围和发生时间,再用负载、CPU、内存、磁盘、网络指标确定瓶颈方向,然后定位到具体进程、线程或连接,最后结合应用日志、变更记录和流量验证根因。排查顺序是先系统、再进程、最后代码和依赖。
45. 磁盘 I/O 很高,怎么排查?
先确认是哪个磁盘慢,再找谁在读写:
iostat-x1pidstat-d1iotopiostat -x中常关注:
await I/O 请求的平均等待时间 %util 设备忙碌程度 r/s 每秒读请求数 w/s 每秒写请求数不要只凭%util一个数字下结论。SSD、并行队列和云盘环境下,设备特性差别很大。要把延迟、吞吐、队列和业务响应时间放在一起看。
找到进程后,再判断是日志暴增、数据库随机 I/O、大文件扫描、频繁fsync,还是内存不足导致 Swap。
面试回答:先用
iostat -x判断设备延迟、吞吐和繁忙程度,再用pidstat -d或iotop找到高 I/O 进程,最后结合应用行为判断是日志、数据库、文件扫描、刷盘还是 Swap。不能只看单个%util指标。
46. 日志应该从哪里查?
服务由 systemd 管理时,可以先看:
systemctl status service-name journalctl-uservice-name journalctl-uservice-name--since"30 minutes ago"传统日志常放在/var/log,应用也可能写到自己的日志目录。内核问题看:
dmesgjournalctl-k排查时先对齐时间。服务器时区、容器时区和应用日志时区不一致,常让人追错十几分钟甚至几个小时。
面试回答:systemd 服务可以通过
systemctl status和journalctl -u查看,传统系统日志常在/var/log,内核问题看dmesg或journalctl -k。分析时要先确认时间范围和时区,再关联告警、发布和请求日志。
面试前十分钟,把整栋 Linux 大楼画回来
凌晨三点零八分,订单接口恢复。
故障链路并不神秘:日志轮转脚本删掉了旧日志,但 Java 进程仍握着原文件描述符;磁盘空间没有释放,后续写入开始失败;部分请求反复重试,存储 I/O 和系统负载随之升高。
逆境在值班记录里画下这张图:
用户命令 ↓ Shell 与应用 ↓ 系统调用 Linux 内核 ├─ 调度进程和线程 ├─ 映射虚拟内存 ├─ 管理 inode 与文件描述符 └─ 处理磁盘和网络 I/O这几条线能串起大部分 Linux 面试题。
看到 CPU,就想到运行队列、上下文切换和 Load Average;看到内存,就想到虚拟地址、分页、Page Cache、Swap 和 OOM;看到文件,就想到目录项、inode、文件描述符;看到高负载但 CPU 不高,就去找D状态和 I/O。
一页背诵提纲
1. 系统 用户程序通过系统调用进入内核 一切皆文件说的是统一接口 /etc 看配置,/var/log 看日志,/proc 看进程和内核 2. 进程 进程管资源,线程被调度 fork 创建,exec 替换,fork 常用写时复制 R 运行或就绪,S 可中断睡眠,D 不可中断睡眠,Z 僵尸 僵尸已经退出未回收,孤儿仍在运行但父进程已退出 kill 默认发 TERM,kill -9 发不可捕获的 KILL 3. CPU Load Average 统计 R 和 D 状态任务 负载要结合核数、CPU 使用率和持续时间 us 看用户态,sy 看内核态,wa 看 I/O 等待 4. 内存 虚拟地址通过页表映射到物理页 Minor Fault 通常不读盘,Major Fault 需要加载数据 free 小不等于内存不足,重点看 available Page Cache 可回收,持续 Swap 才值得警惕 OOM 是内核在严重内存不足时的自救 5. 文件 文件名在目录项里,元数据和数据索引在 inode 里 硬链接共享 inode,软链接保存目标路径 文件描述符是进程访问文件和 Socket 的整数句柄 df 看已分配块,du 看目录中可见文件 文件删了仍占空间,通常是进程还开着它 6. I/O 与网络 非阻塞不等于异步 epoll 适合大量连接、少量活跃 LT 会重复提醒,ET 要读到 EAGAIN TIME-WAIT 常见于主动关闭方 CLOSE-WAIT 太多,常查应用是否忘记关闭连接 7. 排障 先确认范围,再分 CPU、内存、磁盘、网络 从系统指标定位到进程、线程和连接 最后结合日志、变更和流量确认根因最后一次自测
合上文章,试着回答:
- 为什么 Load Average 很高,CPU 使用率却可能不高?
fork()为什么不需要立刻复制父进程的全部物理内存?- 僵尸进程和孤儿进程分别处于什么状态?
- 为什么
kill -9也可能无法让D状态进程立刻退出? free很小,为什么不能直接判断内存不足?- 文件删除后,
df为什么可能不下降? - 硬链接和软链接在 inode 上有什么区别?
- 为什么 ET 模式必须配合非阻塞 I/O,并读到
EAGAIN? - 大量
TIME-WAIT和大量CLOSE-WAIT,哪个更像应用代码没有关闭连接? - 如果服务器整体变慢,你会按什么顺序排查?
能把这些问题用自己的话讲明白,就不用把四十多道答案背成口令。面试官换一种问法,你仍然能从运行链路里把答案推回来。