Appearance
Linux 常用命令与排查
面试问 Linux 一般有两个目的:考察你日常是否真的用过服务器,以及遇到线上问题时有没有排查思路。
Q1: 怎么查看系统的 CPU、内存、负载? 「🟢 校招/初级」
考察点:最基础的监控命令。
参考答案:
top/htop:实时查看 CPU、内存、进程占用;关注 load average(1/5/15 分钟)与核数的比值。free -h:查看内存和 swap;注意 buffer/cache 也算"可用"。vmstat 1:每秒输出一次,看上下文切换(cs)、中断、IO 等待(wa)。uptime:快速看负载。
追问延伸:
- load average 高但 CPU 使用率不高,可能是什么原因?(IO 等待)
- top 里的
%wa、%si分别代表什么?
Q2: 怎么排查一个 Java 进程 CPU 占用过高? 「🟡 中级」
考察点:经典的"三步定位法",出现频率极高。
参考答案:
top找到高 CPU 的进程,记下 PID。top -Hp <pid>找到高 CPU 的线程,记下 TID。printf "%x\n" <tid>转十六进制,用jstack <pid> | grep <十六进制tid> -A 20定位到具体代码栈。
- 工具替代:Arthas 的
thread -n 3一步到位。
追问延伸:
- 如果发现是 GC 线程占 CPU 高,接下来怎么查?(jstat/jmap 看内存)
- Go 程序怎么排查?(pprof)
Q3: 磁盘和文件相关命令? 「🟢 校招/初级」
考察点:日常运维操作。
参考答案:
df -h:查看各分区磁盘使用率;du -sh *:统计当前目录各子目录大小。ls -lh、find / -name "*.log" -size +100M:查找大文件。tail -f app.log:实时看日志;grep -C 3 ERROR app.log:带上下文搜索;less分页。ln -s:软链接;chmod、chown:权限。
追问延伸:
- 磁盘满了但
du统计对不上,可能是什么原因?(已删除但未释放的文件句柄,lsof | grep deleted) - 硬链接和软链接的区别?
Q4: 网络排查命令有哪些? 「🟡 中级」
考察点:定位网络问题的工具箱。
参考答案:
curl -v:看完整请求响应过程;ping、traceroute:连通性与路径。netstat -anp | grep <port>/ss -lntp:查看端口监听和连接状态。dig/nslookup:DNS 解析验证。tcpdump -i eth0 port 8080 -w dump.pcap:抓包,配合 Wireshark 分析。lsof -i :8080:看哪个进程占用了端口。
追问延伸:
ss比netstat快在哪?- 大量 CLOSE_WAIT 连接说明什么问题?
Q5: 常用的进程与文本处理命令? 「🟢 校招/初级」
考察点:shell 基本功。
参考答案:
- 进程:
ps -ef | grep java、kill -15(优雅)/kill -9(强制)、nohup、&后台。 - 文本三剑客:
grep(过滤)、sed(行编辑替换)、awk(按列统计)。 - 示例:
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head统计访问 IP Top10。
追问延伸:
kill -9为什么有时候杀不掉进程?(D 状态/僵尸进程)- 怎么让脚本异常退出时自动清理临时文件?(trap)
Q6: 怎么查看和管理 systemd 服务? 「🟡 中级」
考察点:现代 Linux 服务管理方式。
参考答案:
systemctl status/start/stop/restart <service>:服务管理。systemctl enable:开机自启;journalctl -u <service> -f:看服务日志。- 服务定义在
/etc/systemd/system/*.service,可配置重启策略、资源限制。
追问延伸:
- 和传统 init.d 脚本的区别?
- 容器场景下还需要 systemd 吗?(一般用容器自身的进程管理)
Q7: ps 命令有哪些常用选项?展示了哪些信息? 「🟡 中级」
考察点:进程查看的基本功。
参考答案:
常用选项:
ps -ef:查看所有进程,显示 UID、PID、PPID、C(CPU使用率)、STIME(启动时间)、TTY、TIME、CMDps aux:BSD 风格,显示 USER、PID、%CPU、%MEM、VSZ(虚拟内存)、RSS(物理内存)、STAT(状态)、START、TIME、COMMANDps -ef --forest:树形显示进程关系(父子进程)ps -eLf:查看所有线程(LWP 列为线程 ID)
STAT 状态含义:
R:运行/就绪S:可中断睡眠(等待事件)D:不可中断睡眠(通常在等 IO,kill -9 也杀不掉)Z:僵尸进程(父进程没回收)T:暂停/被跟踪+:前台进程组s:会话组长l:多线程<:高优先级N:低优先级
RSS vs VSZ:
- VSZ:虚拟内存大小(含 swap 和映射文件,通常偏大)
- RSS:常驻物理内存大小(实际占用的物理内存,更有参考价值)
追问延伸:
- 僵尸进程怎么处理?(找父进程 kill,或修复父进程的 wait 回收逻辑)
- D 状态进程为什么 kill -9 无效?
Q8: 已知进程名,如何杀掉这个进程? 「🟢 校招/初级」
考察点:日常运维操作。
参考答案:
方法一:两步走(先查 PID 再 kill)
bash
# 查 PID
ps -ef | grep nginx | grep -v grep
# 或
pgrep -f nginx
# 杀进程
kill -15 <pid> # 优雅终止(SIGTERM,进程可以清理后退出)
kill -9 <pid> # 强制杀死(SIGKILL,不可拦截)方法二:一步到位
bash
pkill -f nginx # 按进程名杀(发 SIGTERM)
pkill -9 -f nginx # 强制杀(发 SIGKILL)
killall nginx # 按精确进程名杀kill 信号区别:
| 信号 | 编号 | 含义 | 可拦截 |
|---|---|---|---|
| SIGTERM | 15 | 优雅终止 | 是 |
| SIGKILL | 9 | 强制杀死 | 否 |
| SIGHUP | 1 | 挂断/重载配置 | 是 |
| SIGINT | 2 | Ctrl+C 中断 | 是 |
| SIGQUIT | 3 | Ctrl+\ 退出 | 是 |
生产建议:先 kill -15,等 5-10 秒没退出再 kill -9。
追问延伸:
- kill -9 杀不掉的进程怎么办?(D 状态等 IO,需修复 IO 或重启)
- pkill 和 killall 的区别?
Q9: 怎么查看哪个端口被哪个进程占用?端口连通性怎么测? 「🟡 中级」
考察点:端口排查的常用命令。
参考答案:
查看端口占用:
bash
# 方法1:lsof(最常用)
lsof -i :8080 # 查看 8080 端口被哪个进程占用
# 方法2:netstat
netstat -tunlp | grep 8080 # -t TCP -u UDP -n 不解析域名 -l 监听 -p 进程
# 方法3:ss(比 netstat 快)
ss -lntp | grep 8080
# 方法4:fuser
fuser 8080/tcp # 查看占用 8080 的进程 PID端口连通性测试:
bash
# TCP 端口
telnet 192.168.1.1 8080 # 旧但通用
nc -zv 192.168.1.1 8080 # netcat,推荐
curl -v telnet://192.168.1.1:8080 # curl 也可以
# UDP 端口
nc -zuv 192.168.1.1 161
# 批量扫描
nmap -p 80,443,8080 192.168.1.1ss 比 netstat 快的原因:
- netstat 遍历 /proc 下的所有 fd,进程多时很慢
- ss 直接读取内核 socket 缓存,不依赖 /proc
追问延伸:
- 端口不通怎么排查?(防火墙、安全组、服务没启动、bind 地址不对)
- TIME_WAIT 和 CLOSE_WAIT 大量出现分别说明什么?
Q10: top 和 free 都能看内存,有什么区别? 「🟡 中级」
考察点:系统监控工具的深入理解。
参考答案:
free -h 输出:
total used free shared buff/cache available
Mem: 7.8G 2.1G 1.2G 0.1G 4.5G 5.3G
Swap: 2.0G 0B 2.0Gtotal:物理内存总量used:已使用(不含 buffer/cache)free:完全空闲buff/cache:系统缓存(可以回收)available:实际可用 ≈ free + 可回收的 buffer/cache
top 内存部分:
VIRT:虚拟内存总量(含映射文件、共享库,偏大,参考价值低)RES:常驻物理内存(实际占用的物理内存,有参考价值)SHR:共享内存
区别:
free看系统整体内存,关注 available(不是 free!)top看单个进程内存,关注 RES- 判断内存够不够:看
available,不是free(buffer/cache 是可以回收的)
内存是否够用的判断:
available> 总量的 20% → 够用available< 总量的 10% → 需要关注- Swap 在使用 → 内存不够,系统在用交换分区(性能严重下降)
si/so(vmstat)持续大于 0 → 内存紧张
追问延伸:
- buffer 和 cache 的区别?(buffer 是写缓存,cache 是读缓存)
- 为什么 free 很少但系统还正常?
Q11: 服务器 CPU 跑到 100%,排查思路是什么? 「🔴 高级」
考察点:线上故障排查的综合能力。
参考答案:
排查流程(从整体到细节):
确认是哪种 CPU 100%:
top看%us(用户态)、%sy(内核态)、%wa(IO等待)、%si(软中断)%us高 → 应用代码问题(死循环、GC、加密计算)%sy高 → 系统调用频繁(大量线程切换、锁竞争)%wa高 → 磁盘 IO 瓶颈(不是 CPU 不够,是等 IO)%si高 → 网络中断频繁(大量小包、网卡多队列未开启)
定位到进程:
top按 CPU 排序,找到高 CPU 进程 PID
定位到线程(Java 应用):
top -Hp <pid>找到高 CPU 线程 TIDprintf "%x\n" <tid>转十六进制jstack <pid> | grep <hex_tid> -A 30看线程栈- 或用 Arthas:
thread -n 3
非 Java 应用:
perf top:看 CPU 热点函数strace -p <pid> -c:看系统调用统计pidstat -t -p <pid> 1:按线程看 CPU
常见原因:
- 死循环 / 无限重试
- Full GC 频繁(
jstat -gc <pid> 1000) - 正则表达式回溯爆炸
- 大量日志写入
- 加密/序列化消耗
- 锁竞争导致大量自旋
临时缓解:
- 限流 / 降级
- 重启进程(治标不治本)
- 扩容到更多节点
追问延伸:
- %wa 高但 CPU 不忙,怎么排查磁盘 IO?
- 怎么区分是 CPU 不够还是应用有 bug?
Q12: Git rebase 和 merge 有什么区别? 「🟡 中级」
考察点:Git 工作流的理解和选型。
参考答案:
核心区别:
merge:把两个分支的修改合并成一次新提交,保留所有历史记录
- 产生一个 merge commit(有多个父提交)
- 历史记录是"有向无环图"(非线性的)
- 优点:历史真实完整、操作安全
- 缺点:历史记录杂乱(大量 merge commit)
rebase:把当前分支的提交"搬到"目标分支的最新提交之后
- 变基:把当前分支的每个 commit 重新应用到目标分支末尾
- 历史记录是线性的(干净整洁)
- 优点:历史简洁清晰
- 缺点:重写了历史(commit hash 变了),有冲突时要逐个 commit 解决
图示对比:
# merge
A---B---C feature
/ \
*---D---E---M main
# rebase
A'--B'--C' feature
/
*---D---E main使用场景:
merge:feature 分支合入 main(保留 feature 的历史)rebase:feature 分支拉取 main 的最新代码(保持 feature 历史线性)- 团队规范:很多团队要求 PR 合并前先 rebase main,保持历史干净
黄金法则:不要 rebase 已经推送到远程且其他人正在使用的分支(会覆盖别人的历史)。
bash
# merge
git checkout main
git merge feature
# rebase
git checkout feature
git rebase main
# 解决冲突后
git rebase --continueSquash Merge(第三种选择):
git merge --squash feature:把 feature 的多个 commit 压缩成一个- 适合:feature 分支有很多临时 commit,合并时只想保留一个干净的 commit
追问延伸:
- rebase 有冲突怎么办?(逐个 commit 解决,
git rebase --continue) - cherry-pick 和 rebase 的区别?
Q13: Git 怎么合并分支?冲突了怎么解决? 「🟢 校招/初级」
考察点:日常 Git 操作。
参考答案:
合并分支:
bash
# 切到目标分支
git checkout main
# 合并 feature 分支
git merge feature冲突解决流程:
- Git 报冲突,文件中标记冲突区域:
<<<<<<< HEAD
当前分支的代码
=======
被合并分支的代码
>>>>>>> feature- 手动编辑文件,保留正确的代码,删除冲突标记
- 标记冲突已解决:
bash
git add <冲突文件>- 继续合并:
bash
git commit # merge 方式
git rebase --continue # rebase 方式冲突产生的本质:两个分支修改了同一文件的同一区域,Git 无法自动决定用哪个。
减少冲突的技巧:
- 频繁拉取主干代码(
git pull --rebase) - 拆分大 PR,减少改动范围
- 团队分工明确,避免改同一文件
- 代码格式化工具统一格式
追问延伸:
- 怎么撤销一个有冲突的 merge?(
git merge --abort) - 怎么查看冲突的文件列表?(
git status)
Q14: 怎么撤回已经提交的 Git commit? 「🟡 中级」
考察点:Git 历史修改的操作能力。
参考答案:
三种撤销方式:
git reset(最常用):
bash
# 撤回最近1个 commit,保留修改在工作区
git reset HEAD~1 # 默认 --mixed
# 撤回最近1个 commit,修改放回暂存区
git reset --soft HEAD~1
# 撤回最近1个 commit,丢弃所有修改(危险!)
git reset --hard HEAD~1- 适用:还没 push 的 commit
- 注意:
--hard会丢失代码
git revert(安全撤销):
bash
# 生成一个反向 commit,撤销指定 commit 的修改
git revert <commit-hash>- 适用:已经 push 的 commit(不改写历史,新增一个"撤销"提交)
- 安全:多人协作时用这个
git commit --amend(修改最后一次提交):
bash
# 修改最后一次 commit 的信息或追加文件
git add <遗漏的文件>
git commit --amend -m "新的commit信息"- 适用:还没 push,想修改 commit 信息或补充文件
- 注意:amend 会改变 commit hash
对比:
| 方式 | 是否改写历史 | 是否安全 | 适用场景 |
|---|---|---|---|
| reset --soft | 是 | 本地安全 | 撤回未push的commit,保留修改 |
| reset --hard | 是 | 危险 | 完全丢弃修改 |
| revert | 否 | 最安全 | 撤回已push的commit |
| amend | 是 | 本地安全 | 修改未push的commit |
已 push 的 commit 撤销:
- 推荐
git revert(不改写历史) - 如果是个人分支可以
git reset --hard + git push --force(覆盖远程) - 绝对不要 force push 到 main/master 分支
追问延伸:
git reflog是什么?(记录所有 HEAD 变更,可以恢复误操作)- 怎么找回 reset --hard 丢弃的代码?(
git reflog找到之前的 commit hash)
Q15: Linux 文件权限体系是怎样的?特殊权限位有哪些? 「🟡 中级」
考察点:对 Linux 权限模型的的理解深度。
参考答案:
基本权限:
$ ls -l file
-rwxr-xr-- 1 root root 1024 Aug 24 10:00 file
│└─┬─┘└─┬─┘└─┬─┘
│ │ │ └── 其他用户(Other):r--
│ │ └────── 组用户(Group):r-x
│ └────────── 所有者(User):rwx
└───────────── 文件类型(- 普通文件,d 目录,l 软链接)权限含义:
| 权限 | 文件 | 目录 |
|---|---|---|
| r | 读取内容 | 列出目录内容(ls) |
| w | 修改内容 | 在目录中创建/删除文件 |
| x | 执行 | 进入目录(cd) |
chmod 数字表示法:
bash
chmod 755 file # rwxr-xr-x
chmod 644 file # rw-r--r--
chmod -R 750 dir # 递归设置目录权限- r=4, w=2, x=1,三者之和就是数字
特殊权限位(3 个):
SUID(Set UID)= 4:
- 作用在可执行文件上:执行时以文件所有者身份运行
- 典型:
/usr/bin/passwd(普通用户执行时以 root 身份修改密码) chmod u+s file或chmod 4755 file
SGID(Set GID)= 2:
- 作用在目录上:在该目录下创建的文件继承目录的组
- 典型:共享目录,所有人创建的文件都属于同一组
chmod g+s dir或chmod 2755 dir
Sticky Bit = 1:
- 作用在目录上:只有文件所有者和 root 才能删除该目录下的文件
- 典型:
/tmp目录,所有人可写但只能删自己的文件 chmod o+t dir或chmod 1777 dir
umask(默认权限掩码):
- 文件默认权限 = 666 - umask(如 umask=022 → 644)
- 目录默认权限 = 777 - umask(如 umask=022 → 755)
umask 027设置临时 umask
追问延伸:
- 为什么
passwd命令普通用户可以修改 /etc/shadow?(SUID 机制) - /tmp 目录权限是 1777,1 代表什么?
Q16: 写一个 Shell 脚本,统计 Nginx access.log 中访问量最高的 10 个 IP 「🟢 校招/初级」
考察点:文本处理能力 + Shell 基本功。
参考答案:
bash
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10逐步解析:
awk '{print $1}':提取第一列(IP 地址),以空格/Tab 分隔sort:排序(相同 IP 排在一起,为 uniq 做准备)uniq -c:去重并统计次数(-c 表示 count)sort -rn:按数字倒序排序(-r reverse,-n numeric)head -10:取前 10 行
Shell 脚本基础语法:
bash
#!/bin/bash
# 变量
name="hello" # 赋值,等号两边不能有空格
echo $name # 使用变量
echo ${name}world # 字符串拼接
# 条件判断
if [ -f /etc/passwd ]; then
echo "file exists"
elif [ -d /tmp ]; then
echo "dir exists"
else
echo "not found"
fi
# 循环
for i in 1 2 3 4 5; do
echo $i
done
for ip in $(cat ips.txt); do
ping -c 1 $ip > /dev/null && echo "$ip OK" || echo "$ip FAIL"
done
while read line; do
echo $line
done < file.txt
# 函数
check_port() {
local port=$1
ss -lntp | grep ":$port " > /dev/null && return 0 || return 1
}
check_port 8080 && echo "running" || echo "not running"
# 数组
arr=(apple banana cherry)
echo ${arr[0]} # apple
echo ${arr[@]} # 所有元素
echo ${#arr[@]} # 数组长度常用内置变量:
| 变量 | 含义 |
|---|---|
$0 | 脚本名 |
$1-$9 | 位置参数 |
$# | 参数个数 |
$@ | 所有参数 |
$? | 上一条命令退出码(0 成功) |
$$ | 当前进程 PID |
$! | 后台进程 PID |
追问延伸:
[ ]和[[ ]]的区别?([[ ]]是 Bash 扩展,支持通配符匹配和逻辑运算符)- 脚本调试怎么做?(
bash -x script.sh显示每步执行过程)
Q17: Linux 定时任务怎么做?crontab 和 systemd timer 有什么区别? 「🟡 中级」
考察点:定时任务管理能力。
参考答案:
crontab 方式(最常用):
bash
# 编辑当前用户的定时任务
crontab -e
# 查看定时任务
crontab -l
# 格式:分 时 日 月 周 命令
# ┌───── 分 (0-59)
# │ ┌───── 时 (0-23)
# │ │ ┌───── 日 (1-31)
# │ │ │ ┌───── 月 (1-12)
# │ │ │ │ ┌───── 周 (0-7, 0和7都是周日)
# │ │ │ │ │
* * * * * /path/to/command # 每分钟
0 2 * * * /backup.sh # 每天凌晨 2 点
*/10 * * * * /check.sh # 每 10 分钟
0 9 * * 1-5 /work.sh # 周一到周五 9 点
0 0 1 * * /month_report.sh # 每月 1 号 0 点crontab 注意事项:
- 命令要写绝对路径(环境变量不一定加载)
- 日志重定向:
>> /var/log/myjob.log 2>&1,否则输出会邮寄给用户 - 脚本里
source ~/.bashrc加载环境变量 - 最小粒度是分钟,秒级需要其他方案
systemd timer 方式(更现代):
ini
# /etc/systemd/system/myjob.timer
[Unit]
Description=Run myjob every 10 minutes
[Timer]
OnCalendar=*:0/10 # 每 10 分钟
Persistent=true # 错过的任务开机后补执行
[Install]
WantedBy=timers.targetini
# /etc/systemd/system/myjob.service
[Unit]
Description=My backup job
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.shbash
systemctl enable myjob.timer
systemctl start myjob.timer
systemctl list-timers # 查看所有 timer| 维度 | crontab | systemd timer |
|---|---|---|
| 精度 | 分钟级 | 秒级 |
| 日志 | 自行管理 | journalctl 统一管理 |
| 依赖 | 无 | 可配置依赖关系 |
| 错过补偿 | 不支持 | Persistent=true 支持 |
| 资源限制 | 无 | 可配 CPU/内存限制 |
| 适用 | 简单定时 | 生产级定时任务 |
追问延伸:
- 秒级定时任务怎么实现?(systemd timer、while+sleep 循环、消息延迟队列)
- crontab 任务没执行怎么排查?(cron 服务是否运行、路径问题、权限、日志
/var/log/cron)
Q18: IO 重定向和管道是什么? tee 和 xargs 怎么用? 「🟢 校招/初级」
考察点:Shell IO 机制的理解。
参考答案:
Linux 三个标准流:
| 描述符 | 名称 | 默认 |
|---|---|---|
| 0 | stdin | 键盘输入 |
| 1 | stdout | 终端屏幕 |
| 2 | stderr | 终端屏幕 |
重定向操作符:
bash
# 输出重定向
echo "hello" > file.txt # 覆盖写入
echo "world" >> file.txt # 追加写入
# 错误重定向
command 2> error.log # 只重定向错误
command > /dev/null 2>&1 # 正常和错误都丢弃
command &> /dev/null # 同上(简写)
# 输入重定向
wc -l < /etc/passwd # 从文件读取标准输入
mysql -u root < dump.sql # 从文件读取 SQL
# Here Document
cat <<EOF
多行文本
变量会被展开:$HOME
EOF
# Here String
grep "pattern" <<< "some text"管道(|):
bash
# 前一个命令的 stdout 作为后一个命令的 stdin
cat access.log | grep "ERROR" | wc -l
ps -ef | grep java | grep -v greptee 命令(同时输出到屏幕和文件):
bash
# 管道中间分流:既看屏幕又存文件
command | tee output.txt
# 追加模式
command | tee -a output.txt
# 同时输出到多个文件
command | tee file1.txt file2.txtxargs 命令(把 stdin 转为命令参数):
bash
# 找到所有 .log 文件并删除
find /var/log -name "*.log" | xargs rm -f
# 批量 kill 进程
ps -ef | grep java | grep -v grep | awk '{print $2}' | xargs kill -15
# 批量 ping
cat hosts.txt | xargs -I{} ping -c 1 {}
# -n 限制每次传入的参数个数
echo "1 2 3 4 5" | xargs -n 2 echo
# 输出:1 2
# 3 4
# 5
# -P 并行执行
find . -name "*.py" | xargs -P4 -I{} python {}常见陷阱:
xargs默认以空格/换行分隔参数,文件名含空格会出错- 解决:
find . -name "*.txt" -print0 | xargs -0 grep "pattern"
- 解决:
- 管道只传递 stdout,stderr 不在管道中(需
2>&1先合并)
追问延伸:
|和;的区别?(管道传递数据,分号顺序执行不传递)> file 2>&1和2>&1 > file有什么区别?(顺序不同,结果不同)
Q19: Git stash、cherry-pick、tag 的使用场景是什么? 「🟡 中级」
考察点:Git 高级操作的实际使用能力。
参考答案:
git stash(暂存工作区):
bash
# 正在 feature 分支开发,突然要修 main 上的 bug
git stash # 暂存当前未提交的修改
git checkout main # 切到 main 修 bug
git checkout feature # 修完回来
git stash pop # 恢复之前暂存的修改
# 多个 stash
git stash list # 查看暂存列表
git stash apply stash@{1} # 恢复指定 stash(不删除)
git stash drop stash@{0} # 删除指定 stash
git stash clear # 清空所有 stash- 场景:临时切换分支,不想 commit 半成品
- 注意:stash 只暂存已 tracked 的文件,新文件需
git stash -u
git cherry-pick(摘樱桃):
bash
# 把某个 commit 的修改应用到当前分支
git cherry-pick <commit-hash>
# 把一段 commit 范围摘过来
git cherry-pick <start-commit>..<end-commit>
# 多个 commit
git cherry-pick <hash1> <hash2> <hash3>- 场景:在 release 分支上只合入某个 hotfix,不带其他 feature
- 和 rebase 的区别:rebase 搬运整个分支,cherry-pick 只摘个别 commit
git tag(标签):
bash
# 轻量标签
git tag v1.0.0
# 附注标签(推荐,包含作者、日期、说明)
git tag -a v1.0.0 -m "正式发布 1.0.0 版本"
# 给历史 commit 打标签
git tag -a v0.9.0 <commit-hash> -m "回溯打标签"
# 推送标签到远程
git push origin v1.0.0 # 推单个标签
git push origin --tags # 推所有标签
# 删除标签
git tag -d v1.0.0 # 删本地
git push origin --delete v1.0.0 # 删远程- 场景:版本发布标记,CI/CD 触发条件
- 轻量标签 vs 附注标签:附注标签是完整对象,有元数据,适合正式发布
git submodule(子模块):
bash
# 在主仓库中引入子仓库
git submodule add https://github.com/user/lib.git libs/lib
# 克隆含子模块的仓库
git clone --recursive <url>
# 或先 clone 再初始化
git submodule update --init --recursive
# 更新子模块
git submodule update --remote- 场景:多个项目共享一个公共库(如 SDK、配置)
追问延伸:
- stash 和 commit 到临时分支哪个更好?(临时分支更安全,stash 容易遗忘)
- cherry-pick 冲突了怎么办?(
git cherry-pick --continue或--abort)
Q20: Git 工作流模型有哪些?你们团队用的哪种? 「🔴 高级」
考察点:团队协作模式的理解和选型能力。
参考答案:
主流工作流模型:
1. GitFlow(经典但较重):
┌───── hotfix ─────┐
│ │
main ────●───────────────────●────────
\ ┌── release ──┐
\ / \
develop ───●──●──●●──────────────●●─
\ ┌── feature ──┐
●──●●─●────────────●- 分支:main(生产)、develop(开发)、feature(功能)、release(预发布)、hotfix(紧急修复)
- 优点:分支职责清晰,适合版本发布的传统产品
- 缺点:分支多、合并复杂,不适合持续部署
2. GitHub Flow(轻量):
main ────●────────────────●────────
\ /
feature ──●──●──●──●──PR──- 只有 main + feature 分支,通过 PR 合并
- main 随时可部署,feature 合入前需 CI 通过
- 优点:简单、适合持续部署
- 缺点:没有预发布环节
3. Trunk-Based Development(主干开发):
main ──●──●──●──●──●──●──●──●──
\ / /
short ● ●
feature- 所有人直接往 main 提交(或极短生命周期的 feature 分支)
- 依赖 feature flag + CI/CD + 快速回滚
- 优点:合并冲突少、部署快、适合高频发布
- 缺点:要求 CI 完善、代码质量高、feature flag 管理
- 代表:Google、Facebook、Netflix
4. GitLab Flow(环境分支):
production ──●────●────●──
↑ ↑
pre-prod ──●────●────●──
↑ ↑
develop ──●────●────●──- develop → pre-prod → production 环境分支
- 优点:环境和代码绑定,部署可追溯
- 缺点:环境分支合并容易冲突
选型建议:
| 团队特征 | 推荐工作流 |
|---|---|
| 小团队 + 持续部署 | GitHub Flow |
| 大团队 + 持续部署 + CI 强 | Trunk-Based |
| 版本发布型产品(如客户端) | GitFlow |
| 多环境部署需求 | GitLab Flow |
Feature Flag(特性开关)的作用:
- 不通过分支控制功能可见性,而通过运行时配置开关
- 代码先合入 main,功能默认关闭,验证后打开开关
- 是 Trunk-Based 的关键支撑技术
追问延伸:
- 为什么 Google 用 Trunk-Based 而不用 GitFlow?(数万人协作,分支模型必须极简)
- Feature Flag 怎么管理?(配置中心统一管理,如 Apollo/Nacos)
Q21: top 命令输出每一行都代表什么意思? 「🟢 校招/初级」
考察点:对系统监控工具输出的理解。
参考答案:
top 命令输出分两部分:上半部分是系统信息,下半部分是进程列表。
top - 14:23:45 up 10 days, 3:15, 2 users, load average: 0.52, 0.45, 0.38
Tasks: 128 total, 1 running, 127 sleeping, 0 stopped, 0 zombie
%Cpu(s): 2.3 us, 1.2 sy, 0.0 ni, 96.3 id, 0.2 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 7864.0 total, 1234.5 free, 2100.0 used, 4529.5 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 5300.0 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 root 20 0 2.5g 1.2g 5000 S 5.2 15.2 0:45.30 java第一行(系统概况):
14:23:45:当前系统时间up 10 days:系统已运行 10 天2 users:当前 2 个用户登录load average: 0.52, 0.45, 0.38:1/5/15 分钟平均负载
第二行(进程概况):
128 total:总进程数1 running:运行中127 sleeping:睡眠中0 stopped:停止0 zombie:僵尸
第三行(CPU 使用率):
us:用户态 CPU(应用代码)sy:内核态 CPU(系统调用)ni:nice 值非 0 的进程占的 CPUid:空闲 CPUwa:IO 等待(磁盘慢会高)hi:硬件中断si:软中断(网络收包多会高)st:被虚拟化偷走的 CPU(云主机常见)
第四、五行(内存和 Swap):
total / free / used / buff/cache:总量 / 空闲 / 已用 / 缓存avail Mem:实际可用内存(含可回收的 buff/cache)
进程列表:
| 列 | 含义 |
|---|---|
| PID | 进程 ID |
| USER | 进程所有者 |
| PR | 优先级(越小越高) |
| NI | nice 值(-20 到 19,负值优先级高) |
| VIRT | 虚拟内存总量 |
| RES | 常驻物理内存(实际占用) |
| SHR | 共享内存 |
| S | 进程状态(R/S/D/Z/T) |
| %CPU | CPU 占用率 |
| %MEM | 内存占用率 |
| TIME+ | 累计 CPU 时间 |
| COMMAND | 启动命令 |
常用交互操作:
1:展开每个 CPU 核心的使用率P:按 CPU 排序M:按内存排序k:输入 PID 杀进程q:退出
追问延伸:
- load average 等于多少算高?(超过 CPU 核数算高,如 4 核 > 4.0)
- 按数字 1 后看到的每个 CPU 核心使用率有什么用?(判断是否单核打满)
Q22: 怎么查看线程状态?一个进程有多少个线程? 「🟡 中级」
考察点:线程级别的排查能力。
参考答案:
方法一:top -H:
bash
# 查看某进程内的线程
top -H -p <pid>
# 或先 top 再按 H 键,显示线程模式
top -H
# 输出会显示 TID(线程ID)而不是 PID方法二:ps 命令:
bash
# 查看某进程的所有线程
ps -T -p <pid>
# 或
ps -eLf | grep <进程名>
# -T 显示线程,-L 显示轻量级进程(线程),-f 完整格式
# 输出包含 PID、LWP(线程ID)、TTY、STAT、TIME、COMMAND方法三:/proc 文件系统:
bash
# 查看进程的线程数
ls /proc/<pid>/task | wc -l
# 查看每个线程的详细信息
ls /proc/<pid>/task/
# 每个数字是一个 TID 目录,里面有 status、stat 等方法四:Java 应用专用:
bash
# jstack 查看线程栈
jstack <pid>
# jcmd 查看线程
jcmd <pid> Thread.print
# Arthas 查看线程
thread -n 3 # CPU 最高的 3 个线程
thread <tid> # 查看某线程栈
thread --state BLOCKED # 查看阻塞线程线程状态对照:
R:运行/就绪S:可中断睡眠D:不可中断睡眠(等 IO)Z:僵尸T:暂停
Java 线程状态(jstack 输出):
RUNNABLE:运行/就绪BLOCKED:等 synchronized 锁WAITING:Object.wait() 等待TIMED_WAITING:Thread.sleep(n) 等待TERMINATED:已结束
追问延伸:
- 线程数过多会有什么问题?(上下文切换开销大、内存占用高、调度延迟)
- 怎么排查线程死锁?(jstack 检测、Arthas
thread --deadlock)
Q23: sed 和 awk 有什么区别?各适合什么场景? 「🟡 中级」
考察点:文本三剑客的理解和使用。
参考答案:
sed(Stream Editor):行级文本编辑器,逐行处理文本。
bash
# 替换字符串(最常用)
sed 's/old/new/g' file.txt # 输出到终端
sed -i 's/old/new/g' file.txt # 原地修改
sed -i.bak 's/old/new/g' file.txt # 原地修改并备份
# 删除空行
sed '/^$/d' file.txt
# 删除第 2-5 行
sed '2,5d' file.txt
# 只打印包含 error 的行
sed -n '/error/p' file.txt
# 在某行前插入
sed '/pattern/i\new line' file.txt- 强项:替换、删除、插入、批量修改
- 简单快速,适合单行操作
awk:列级文本处理工具,支持条件判断、循环、变量。
bash
# 打印第二列
awk '{print $2}' file.txt
# 带条件过滤(第一列 > 100 时打印第二列)
awk '$1 > 100 {print $2}' file.txt
# 统计第二列的总和
awk '{sum += $2} END {print sum}' file.txt
# 指定分隔符
awk -F: '{print $1, $7}' /etc/passwd # 以冒号分隔
# 统计每个 IP 的访问次数(第1列),按次数排序
awk '{count[$1]++} END {for (ip in count) print count[ip], ip}' access.log | sort -rn | head -10
# 多条件判断
awk '$3 > 80 && $5 > 1000 {print $0}' data.txt- 强项:按列操作、统计聚合、条件过滤、复杂逻辑
- 内置变量:
$0(整行)、$1-$n(第n列)、NF(列数)、NR(行号)、FS(分隔符) - 支持 BEGIN/END 块、数组、函数
对比:
| 维度 | sed | awk |
|---|---|---|
| 处理粒度 | 行级 | 列级 |
| 替换 | 强(正则替换) | 可以但不如 sed 方便 |
| 统计/计算 | 弱 | 强(支持加减乘除、聚合) |
| 条件判断 | 有限 | 完整(if/else/循环) |
| 典型场景 | 批量替换、删除行 | 按列提取、统计、分析 |
| 性能 | 较快 | 稍慢(功能更多) |
选择建议:
- 只需替换/删除/插入 →
sed - 需要按列处理/统计/计算 →
awk - 两者经常配合使用:
sed预处理 +awk分析
追问延伸:
- sed 的
-i选项有什么风险?(直接改文件,没有撤销,建议先不加-i验证再执行) - awk 怎么实现多文件处理?(
awk 'FNR==1{...} {...}' file1 file2)
Q24: Shell 实战:查找日志中字符串长度、统计请求耗时 Top10 「🟡 中级」
考察点:实际场景的 Shell 编写能力。
参考答案:
题目一:查找日志中某个字符串出现的长度
bash
# 统计 ERROR 在日志中出现的次数
grep "ERROR" app.log | wc -l
# 查找每行中匹配字符串的长度
grep "search_string" log_file | awk '{print length}'
# length 是 awk 内置函数,返回当前行的字符数
# 查找匹配字符串本身在每行中出现的长度
echo "hello world" | awk '{match($0, /world/); print RLENGTH}'
# RLENGTH 是 awk 内置变量,记录 match() 匹配到的长度题目二:统计请求耗时最高的 10 条记录
日志格式示例:
2024-01-15 192.168.1.100 350
2024-01-15 10.0.0.5 1200
2024-01-15 172.16.0.3 80第一列:日期,第二列:IP,第三列:耗时(ms)
bash
# 方法一:sort 排序
sort -k3 -nr app.log | head -n 10
# -k3:按第三列排序
# -n:数字排序(否则 9 > 100)
# -r:倒序(从大到小)
# head -n 10:取前 10 条
# 方法二:awk + sort
awk '{print $3, $0}' app.log | sort -nr | head -10 | cut -d' ' -f2-
# 先把第三列放前面排序,再去掉临时列
# 方法三:awk 一行搞定(利用数组)
awk '{a[NR]=$3; b[NR]=$0} END {
# 简单冒泡取 Top10(小数据量适用)
for (i=1; i<=NR; i++)
for (j=i+1; j<=NR; j++)
if (a[j] > a[i]) { tmp=a[i]; a[i]=a[j]; a[j]=tmp;
tmp=b[i]; b[i]=b[j]; b[j]=tmp }
for (i=1; i<=10 && i<=NR; i++) print b[i]
}' app.log题目三:统计访问量最高的 10 个 IP(经典题)
bash
awk '{print $2}' app.log | sort | uniq -c | sort -rn | head -10题目四:找出 5xx 错误的请求
bash
# 假设是 Nginx access.log,第9列是状态码
awk '$9 ~ /^5/' access.log | head -20
# 统计各状态码数量
awk '{count[$9]++} END {for (code in count) print code, count[code]}' access.log | sort -rn常用技巧总结:
sort -k N -nr:按第 N 列数字倒序排序uniq -c:去重并计数(需先 sort)awk '{count[$N]++}':用关联数组做分组统计head -n N:取前 N 条wc -l:统计行数
追问延伸:
- 日志文件特别大(几十 GB),怎么处理?(
split分片后并行处理,或用awk流式处理) - 怎么实时监控错误日志?(
tail -f app.log | grep --line-buffered ERROR)
Q25: 怎么分析系统负载?load average 到底怎么看? 「🟡 中级」
考察点:负载分析的综合能力。
参考答案:
load average 是什么:
- 系统"平均负载",指单位时间内处于 R 状态(运行+就绪)和 D 状态(不可中断睡眠)的平均进程数
- 三个数字分别是 1 分钟、5 分钟、15 分钟的平均值
- 不一定等于 CPU 使用率:负载包含等 IO 的进程
怎么看 load average:
bash
# 方法一:uptime
$ uptime
14:23:45 up 10 days, 3:15, 2 users, load average: 0.52, 0.45, 0.38
# 方法二:top 命令第一行
$ top -bn1 | head -1
# 方法三:cat /proc/loadavg
$ cat /proc/loadavg
0.52 0.45 0.38 2/128 12345
# 前三个数是 load average
# 第四个数:运行进程数/总进程数
# 第五个数:最近创建的 PID判断负载是否高:
- load average <= CPU 核数 → 正常
- load average > CPU 核数 → 有进程在排队,需要关注
- load average > CPU 核数 × 2 → 明显过载
bash
# 查看 CPU 核数
nproc # 输出核数
lscpu # 详细 CPU 信息
# 在 top 中按数字 1 也能看到核数例:4 核服务器,load average = 3.5 → 正常(接近满载但没超) 例:4 核服务器,load average = 8.0 → 过载(排队严重)
深入分析工具:
bash
# 1. mpstat - 按核查看 CPU 各项指标
mpstat -P ALL 1
# %usr(用户态)、%sys(内核态)、%iowait(IO等待)、%idle(空闲)
# %iowait 高 → 磁盘慢;%sys 高 → 系统调用频繁
# 2. pidstat - 定位具体进程的 CPU 占用
pidstat -u 1 # 所有进程 CPU 使用率
pidstat -u -p <pid> 1 # 指定进程
pidstat -r 1 # 内存使用
pidstat -d 1 # 磁盘 IO
# 3. vmstat - 综合系统状态
vmstat 1
# r:就绪队列长度(持续 > 核数说明 CPU 瓶颈)
# b:D 状态进程数(等 IO)
# cs:上下文切换次数
# in:中断次数
# us/sy/wa/id:CPU 各项百分比
# 4. iostat - 磁盘 IO
iostat -x 1
# %util:磁盘使用率
# await:IO 等待时间load average 高但 CPU 使用率不高的原因:
- IO 瓶颈:大量 D 状态进程等磁盘 →
%wa高 → 升级磁盘或优化 IO - 网络等待:等网络响应的进程也算负载
- 锁竞争:线程等锁也在 R 队列中排队
追问延伸:
- load average 和 CPU 使用率的区别?(负载包含 IO 等待,CPU 使用率只算计算时间)
- vmstat 的
r列和 load average 有什么关系?(r是实时就绪队列,load average 是时间平均)
Q26: 怎么判断内存是否够用?怎么统计内存交换频率? 「🔴 高级」
考察点:内存问题的深度排查能力。
参考答案:
判断内存是否够用:
bash
# 1. free 命令
$ free -m
total used free shared buff/cache available
Mem: 7864 2100 1234 100 4529 5300
Swap: 2048 0 2048判断标准:
available> 总量的 30% → 内存充足available< 总量的 10% → 内存紧张,需要排查available接近 0 → 内存不足- Swap 在使用(used > 0)→ 内存不够,系统在用交换分区 → 性能严重下降
buff/cache大是正常的(系统缓存可以回收)
bash
# 2. 检查是否发生过 OOM(Out of Memory)
dmesg | grep -i "out of memory"
dmesg | grep -i "oom-killer"
# 如果有 OOM 记录,说明内存严重不足,系统杀过进程
# 3. 找出占内存最多的进程
ps aux --sort=-%mem | head -10
# 或
top -bn1 -o %MEM | head -20统计内存交换频率:
bash
# 1. vmstat 看交换活动
$ vmstat 1
procs ---memory-- ---swap-- -----io---- -system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id
0 0 0 123400 45678 789000 0 0 10 5 100 200 2 1 97关键列:
si(swap in):每秒从 swap 读入内存的量(KB),> 0 说明在换入so(swap out):每秒从内存写入 swap 的量(KB),> 0 说明在换出si/so持续 > 0 → 内存紧张,系统在频繁交换 → 性能严重下降
bash
# 2. sar -B 查看页面回收统计
$ sar -B 1
# pgscank/s:kswapd 后台扫描的页面数
# pgscand/s:直接扫描的页面数(应用内存不足时触发,值大说明内存紧张)
# pgsteal/s:回收的页面数
# %vmeff:回收效率(低说明内存碎片严重)
# 3. sar -W 查看 swap 统计
$ sar -W 1
# pswpin/s:每秒换入页数
# pswpout/s:每秒换出页数
# 这两个值持续 > 0 说明内存严重不足
# 4. /proc/meminfo 查看详细信息
$ cat /proc/meminfo | grep -E "SwapTotal|SwapFree|Dirty|Writeback|AnonPages"
# SwapTotal/SwapFree:Swap 总量和空闲
# Dirty:待写入磁盘的脏页
# AnonPages:匿名页(应用内存,不能直接回收)内存不足的排查流程:
free -m看 available 和 Swap 使用vmstat 1看si/so是否 > 0dmesg | grep oom检查 OOM 历史ps aux --sort=-%mem | head找内存大户sar -B 1查看页面回收压力- 对 Java 应用:
jstat -gc <pid>看 GC 情况,jmap -heap <pid>看堆使用
追问延伸:
- 为什么系统还有 free 但 Swap 却在用?(内核的 swapiness 参数,默认 60,会主动把不活跃页换出)
- 怎么临时关闭 swap?(
swapoff -a,但生产环境谨慎,可能 OOM)
Q27: Git 的四个区域是什么?基础工作流是怎样的? 「🟢 校招/初级」
考察点:Git 基础概念。
参考答案:
Git 的四个区域:
工作区 暂存区 本地仓库 远程仓库
(Workspace) (Index/Stage) (Repository) (Remote)
│ │ │ │
│── git add ──→ │ │ │
│ │── git commit →│ │
│ │ │── git push ─→ │
│← git checkout ───────────────│ │
│←────────────── git pull ───────────────────────│工作区(Workspace):
- 你在文件系统中看到的目录和文件
- 修改文件后,变更在工作区
暂存区(Index/Stage):
git add后文件进入暂存区- 暂存区是下一次 commit 的快照
.git/index文件记录
本地仓库(Repository):
git commit后变更进入本地仓库- 存储在
.git目录 - 有完整的提交历史
远程仓库(Remote):
- GitHub/GitLab/Gitee 上的仓库
git push推送到远程,git pull拉取远程
基础工作流:
bash
# 1. 克隆远程仓库
git clone https://github.com/user/repo.git
# 2. 修改文件(工作区变更)
vim file.txt
# 3. 查看变更状态
git status # 查看工作区和暂存区状态
git diff # 查看工作区 vs 暂存区的差异
# 4. 暂存修改
git add file.txt # 暂存单个文件
git add . # 暂存所有变更
# 5. 提交到本地仓库
git commit -m "修复bug描述"
# 6. 推送到远程仓库
git push origin main
# 7. 拉取远程最新代码
git pull # = git fetch + git merge
git pull --rebase # = git fetch + git rebase文件状态流转:
Untracked:新文件,未跟踪 →git add→StagedModified:已跟踪文件被修改 →git add→StagedStaged:已暂存 →git commit→UnmodifiedUnmodified:已提交未修改 → 修改后 →Modified
常用命令对照:
| 操作 | 命令 |
|---|---|
| 查看状态 | git status |
| 查看差异 | git diff / git diff --staged |
| 暂存 | git add <file> / git add . |
| 提交 | git commit -m "msg" |
| 推送 | git push origin <branch> |
| 拉取 | git pull / git pull --rebase |
| 查看日志 | git log --oneline --graph |
| 查看远程 | git remote -v |
| 切换分支 | git checkout <branch> / git switch <branch> |
| 创建分支 | git checkout -b <branch> / git switch -c <branch> |
追问延伸:
git fetch和git pull的区别?(fetch 只下载不合并,pull = fetch + merge)git diff、git diff --staged、git diff HEAD分别比较什么?