目录
- [一、Bash 脚本编程之九:while 循环与循环控制](#一、Bash 脚本编程之九:while 循环与循环控制)
- [二、Bash 脚本编程之十:函数](#二、Bash 脚本编程之十:函数)
- [三、Linux 进程管理之一:进程概念](#三、Linux 进程管理之一:进程概念)
- [四、Linux 进程管理之二:查看与管理进程](#四、Linux 进程管理之二:查看与管理进程)
| 概念 | 一句话 |
|---|---|
| while / until / for | while 条件为真执行;until 条件为假执行;for 遍历列表或范围 |
| break / continue | break 退出整个循环;continue 跳过本轮、进入下一轮 |
| 函数 | 可复用代码块,可传参、可 return 0--255 状态码 |
| 进程 | 程序的一次执行实例;有 PID、状态(R/S/D/T/Z)、优先级 |
| init | PID=1,所有用户进程的祖先;COW 写时复制 |
| ps / top / pstree | 查看进程列表、实时状态、进程树 |
| 信号 | SIGHUP(1) 重读配置、SIGINT(2) 中断、SIGKILL(9) 强杀、SIGTERM(15) 优雅终止 |
| 前台/后台作业 | Ctrl+Z 挂起、& 后台运行、jobs/fg/bg 管理 |
| load average | 1/5/15 分钟平均"可运行+不可中断"进程数,反映系统负载 |
一、Bash 脚本编程之九:while 循环与循环控制 🔄
1.1 三种循环与 break/continue
- while :条件为真(退出码 0)时执行循环体。
- until :条件为假时执行循环体(与 while 条件相反)。
- for:遍历单词列表或数值范围,次数已知或列表已知时常用。
循环控制:
- break :提前退出整个 循环,执行
done后面的语句。 - continue :提前结束本轮循环,直接进入下一轮(本次循环体内 continue 之后的代码不执行)。

| 关键字 | 作用 |
|---|---|
break |
跳出当前循环(嵌套时可用 break 2 跳出外层) |
continue |
跳过本次迭代,回到循环条件重新判断 |
生活例子:while 像"只要还有作业就继续写";until 像"直到闹钟响才停";break 像"写到一半不写了直接交卷";continue 像"这道题跳过,做下一道"。
三种循环对比:

1.2 while 的特殊用法一:死循环 + 读入退出
bash
while :; do
# 循环体
done
生活例子 :
while :死循环像"便利店 24 小时营业"------店一直开着,顾客来一个接待一个,直到有人贴了"停业"告示(break)才关门。
使用场景:
- 交互式工具:反复让用户输入指令,直到用户输入
quit/exit。 - 守护式监控:每隔几秒检查磁盘使用率,超限则报警,永不停止直到手动中断。
- 菜单驱动脚本:反复显示菜单→执行操作→再显示菜单。
示例 :读入文件路径,存在则提示存在,输入 quit 退出。
bash
#!/bin/bash
#
while :; do
read -p "File path:" FILEPATH
[ $FILEPATH == 'quit' ] && break
if [ -e $FILEPATH ]; then
echo "$FILEPATH exists."
else
echo "No $FILEPATH."
fi
done
echo "Quit."
1.3 while 的特殊用法二:按行读文件
生活例子 :
while read LINE按行读文件,像"拿着花名册,从第一行到最后一行逐条念名字";每念一条就做一次判断,是 bash 用户就报出来。
使用场景:
- 逐行处理 CSV / 日志文件(如统计每条访问记录的 IP)。
- 批量创建用户:把"用户名 密码"写在文件里,脚本逐行读取并
useradd。 - 配置文件解析:逐行读取某配置,提取关键值并做处理。
要求 :读取 /etc/passwd,若某行登录 shell 为 /bin/bash,则显示对应用户名。
bash
#!/bin/bash
#
FILE=/etc/passwd
while read LINE; do
[ `echo $LINE | awk -F : '{print $7}'` == '/bin/bash' ] && echo $LINE | awk -F : '{print $1}'
done < $FILE
1.4 练习:为指定网卡创建别名(mkethalias.sh)
说明:脚本为指定网卡创建别名并配置地址。使用格式:mkethalias.sh -v|--verbose -i ethX。
-i指定网卡;若不存在则退出。- 若网卡存在,让用户输入别名(可为空);若别名不空则需确保事先不存在,否则报错并重新输入。
- 别名正确后,让用户输入地址和掩码,并配置到该别名上。
- 若使用
-v,配置完成后显示配置结果;否则不显示。
(脚本具体实现略,保留原题要求。)
相关练习(原稿):
- 将系统安装光盘挂载至
/media/yum目录,用其实现 yum 仓库。 - 配置使用
http://172.16.0.1/yum/{Server,VT,Cluster,ClusterStorage}为可用 yum 仓库。
1.5 练习:同一 repo 文件中创建多个 Yum 源
说明:在同一 repo 文件中创建多个 Yum 源指向。
- 接受一个文件名作为参数,文件放在
/etc/yum.repos.d/,且以.repo结尾;若文件已存在则报错退出。 - 提示用户输入 repo id;若为
quit则退出;否则继续。 - 输入 repo name 和 baseurl,按 repo 文件格式写入该文件。
enabled默认为 1,gpgcheck默认为 0。- 循环执行直到用户输入 repo id 为
quit。
说明 :原稿中有 :.,$s@/etc/yum.repos.d/$1@REPOFILE@g,为 vim 替换命令,用于把脚本中的路径替换为变量 REPOFILE。
参考脚本:
bash
#/bin/bash
#
REPOFILE=/etc/yum.repos.d/$1
if [ -e $REPOFILE ]; then
echo "$1 exists."
exit 3
fi
read -p "Reposiotry ID:" REPOID
until [ $REPOID == 'quit' ]; do
echo "[$REPOID]" >> $REPOFILE
read -p "Repository name:" REPONAME
echo "name=$REPONAME" >> $REPOFILE
read -p "Repository Baseurl:" REPOURL
echo "baseurl=$REPOURL" >> $REPOFILE
echo -e 'enabled=1\ngpgcheck=0' >> $REPOFILE
read -p "Repository ID:" REPOID
done
echo "end..."
(脚本中已使用 baseurl=$REPOURL 以正确写入用户输入的地址。)
1.6 循环控制示例
生活例子:break 像"集市上买鸡蛋,买到 10 个就走,不管后面还有多少摊位";continue 像"挑鸡蛋时发现一个裂的,跳过不要,接着看下一个"。
使用场景:
- break:累加金额达到预算上限就停;扫描日志发现第一条错误就中止并报警。
- continue :遍历文件列表时,跳过隐藏文件(以
.开头的)只处理普通文件;遍历用户时跳过系统用户只处理普通用户。
例 1:求 1~99 中奇数的和。
bash
#!/bin/bash
#
let SUM=0
let I=1
while [ $I -lt 100 ]; do
if [ $[$I%2] -eq 1 ]; then
let SUM+=$I
fi
let I++
done
echo $SUM
例 2:累加 1~100,一旦 SUM>1000 就 break,最后输出当时的 I 和 SUM。
bash
#!/bin/bash
#
declare -i SUM=0
for I in {1..100}; do
let SUM+=$I
if [ $SUM -gt 1000 ]; then
break
fi
done
echo $I
echo $SUM
例 3 :只显示前 6 个 shell 为 /bin/bash 的用户名。
bash
#!/bin/bash
#
FILE=/etc/passwd
let I=0
while read LINE; do
if [ `echo $LINE | awk -F : '{print $7}'` == '/bin/bash' ]; then
echo $LINE | awk -F : '{print $1}'
let I+=1
fi
if [ $I -ge 6 ]; then
break
fi
done < $FILE
例 4:在例 3 基础上,将 UID 小于等于 505 的用户不显示(用 continue 跳过)。
bash
#!/bin/bash
#
FILE=/etc/passwd
let I=0
while read LINE; do
[ `echo $LINE | awk -F : '{print $3}'` -le 505 ] && continue
if [ `echo $LINE | awk -F : '{print $7}'` == '/bin/bash' ]; then
echo $LINE | awk -F : '{print $1}'
let I+=1
fi
if [ $I -ge 6 ]; then
break
fi
done < $FILE
1.7 菜单脚本:根据用户选择显示 UID/GID/SHELL
要求:
- 提示用户输入一个用户名。
- 显示菜单:U|u show UID;G|g show GID;S|s show SHELL;Q|q quit。
- 根据用户选择显示对应内容;非上述选项则提示错误并重新选择。
生活例子:菜单脚本就像"自助点餐机"------屏幕上列出选项,你按一个字母,机器就给你对应的东西;按了不认识的键,机器提醒你重新选。
使用场景:
- 运维工具箱:一个脚本入口,可以选"查磁盘""查内存""重启服务""查日志"等常用操作。
- 新手友好脚本:不需要记命令参数,靠菜单交互即可完成操作。
(实现思路:read 用户名,case 判断选项,用 id 或 grep /etc/passwd 取 UID/GID/SHELL。)
1.8 语法检查脚本:有错误则提示用 vim 编辑
要求:
- 判断指定 bash 脚本是否有语法错误;若有,提示用户输入 Q 或 q 忽略并退出,其他键则用 vim 打开该脚本。
- 若用户保存退出后仍有语法错误,重复步骤 1;否则正常退出。
生活例子:这个脚本像"严格的老师批改作文"------发现语法错误就退回来让你改,改完再检查,直到没错误才放行。
使用场景:
- 开发团队里,提交脚本前自动检查语法,避免把有 bug 的脚本部署到线上。
- 结合
git pre-commit hook,每次 commit 前自动跑一遍语法检查。
bash
# 用法示例:./syntax.sh a.sh
until bash -n $1 &> /dev/null; do
read -p "Syntax error, [Qq] to quit, others for editing: " CHOICE
case $CHOICE in
q|Q)
echo "Something wrong, quiting."
exit 5
;;
*)
vim + $1
;;
esac
done
echo "0K"
二、Bash 脚本编程之十:函数 📦
2.1 函数是什么
- 功能:把一段逻辑封装成可复用的"功能块"。
- 特点 :不能独立运行,需要调用 才执行,可被多次调用。
- 作用:结构化编程、代码重用。
生活例子 :函数就像外卖平台上的"一键下单"按钮------你不需要每次都重新填地址、选菜、付款,一个按钮把所有步骤打包好,按一下就执行。脚本里写好一个函数,后面想用多少次就调多少次,不用重复写相同代码。
使用场景:
- 把"发邮件通知"封装成函数,脚本中磁盘满了调一次、服务挂了调一次、定时巡检完也调一次------逻辑只写一遍。
- 把"检查某服务是否运行"封装,主程序循环检查多个服务时反复调用同一个函数。
- 大型脚本拆分:把日志清理、备份、监控各写成函数,主程序只负责调用顺序。
📜 主程序
📞 调用 函数A
📞 调用 函数A
📦 执行 函数A 体
2.2 定义与调用
方式一:
bash
function FUNCNAME {
command
}
方式二:
bash
FUNCNAME() {
command
}
调用:FUNCNAME(无参)或 FUNCNAME arg1 arg2(有参时在函数内用 $1、$2 等)。
2.3 示例:菜单函数
bash
#!/bin/bash
#
function show_menu {
cat << EOF
d|D) show disk usages
m|M) show memory usages
s|S) show swap usages
q|Q) quit.
EOF
}
show_menu
read -p "You choice:" choice
until [ $choice == 'q' -o $choice == 'Q' ]; do
case $choice in
d|D) df -lh ;;
m|M) free -m | grep "^Mem" ;;
s|S) free -m | grep "^Swap" ;;
*)
show_menu
read -p "Your choice , agin:" choice
esac
show_menu
read -p "Your choice:" choice
done
(原稿中 esac 缩进与 case 对应关系可自行调整。)
2.4 传参与返回值
- 传参 :调用
FUNCNAME a b时,函数内$1=a,$2=b(与脚本参数类似,但只对当前函数有效)。 - 返回值 :
return N(N 为 0~255),调用方用$?获取。
生活例子 :传参像"点咖啡时告诉店员------我要大杯、加冰",函数就是店员,
$1=大杯、$2=加冰;return 像"店员做完告诉你------做好了(0) 或 缺货了(1)",你根据回复决定下一步。
使用场景:
- 传参:写一个
backup函数,参数$1是目录路径、$2是目标路径,主程序对不同目录分别调用。 - 返回值:
check_service httpd,返回 0 表示在线、1 表示挂了,主程序根据返回值决定是否重启或报警。
示例:两数相加。
bash
#!/bin/bash
#
sum() {
echo $[$1+$2]
}
sum 5 6
脚本参数与函数参数 :执行 ./a.sh m n 时脚本中 $1=m、$2=n;在脚本里调用 TWOINT 5 6 时,函数内 $1=5、$2=6。
2.5 练习:ping 探测 192.168.0.200--254 在线主机
要求:
- 用函数实现"对一台主机是否在线"的判定。
- 在主程序中调用该函数,判定范围内所有主机。
写法一:函数内循环 ping 整个网段(无参)。
bash
#!/bin/bash
#
PING() {
for I in {200..254}; do
if ping -c 1 -W 1 192.168.0.$I &> /dev/null; then
echo "192.168.0.$I is up."
else
echo "192.168.0.$I is down."
fi
done
}
PING
写法二:函数接受一个 IP 参数,主程序循环传 IP(推荐)。
bash
#!/bin/bash
#
PING() {
if ping -c 1 -W 1 $1 &> /dev/null; then
echo "$1 is up."
else
echo "$1 is down."
fi
}
for I in {200..254}; do
PING 192.168.0.$I
done
写法三 :函数用 return 返回状态,主程序根据 $? 输出。
bash
#!/bin/bash
#
PING() {
if ping -c 1 -W 1 $1 &> /dev/null; then
return 0
else
return 1
fi
}
for I in {200..254}; do
PING 192.168.0.$I
if [ $? -eq 0 ]; then
echo "192.168.0.$I is up."
else
echo "192.168.0.$I is down."
fi
done
注意 :原稿中 if -c 1 -W 1 ping ... 应改为 if ping -c 1 -W 1 ...。
生活例子:ping 探测像"挨家挨户敲门"------你在走廊里从 200 号房到 254 号房逐个敲门;有人应门就标"在家",没人就标"不在"。函数就是"敲门这个动作",主程序负责挨个走。
使用场景:
- 运维人员快速摸底一个网段里哪些机器在线,做资产盘点。
- 部署前检查目标服务器是否可达。
- 结合定时任务(cron),每 5 分钟检测一遍关键主机,不可达就发告警。
2.6 练习:用户存在则显示 UID 与 SHELL
要求:
- 函数接受一个参数(用户名),判断用户是否存在。
- 若存在:显示该用户的 shell 和 UID,并返回正常状态;若不存在:提示并返回错误状态。
- 主程序调用该函数。
- 扩展 1:主程序让用户输入用户名,再传给函数。
- 扩展 2:输入后不退出,可继续输入下一个用户名;若用户不存在则提示重新输入;输入 q 或 Q 退出。
bash
#!/bin/bash
#
user() {
if id $1 &> /dev/null; then
echo "`grep ^$1 /etc/passwd | cut -d: -f3,7`"
return 0
else
echo "no $1"
return 1
fi
}
read -p "please input username:" username
until [ $username == q -o $username == Q ]; do
user $username
if [ $? == 0 ]; then
read -p "please input again:" username
else
read -p "no $username,please input again:" username
fi
done
2.7 函数与"命令参数"的扩展思路
- 函数可接受"命令名"作为参数,例如根据参数复制对应命令到根文件系统(如
/mnt/sysroot/bin/ls、/mnt/sysroot/sbin/ifconfig),实现定制根文件系统的脚本。(原稿点到为止,此处保留思路。)
三、Linux 进程管理之一:进程概念 ⚙️
3.1 进程与内核、init
- 进程:程序的一次执行实例,有独立的 PID、状态、资源。
- 内核 :负责调度、内存、设备等;init(PID=1)是用户态进程的祖先,由内核启动。
- COW(Copy On Write):写时复制,子进程与父进程共享内存页,直到某一方写入时才复制,节省内存、加快 fork。
生活例子:
- 进程 vs 程序:程序像"菜谱",静静地放在硬盘上;进程像"按菜谱做菜"------同一本菜谱可以同时在两个灶上做,就是两个进程。
- init:像"公司创始人",PID=1,所有其他员工(进程)都是他直接或间接招进来的。
- COW:像"合租房里两个人共用一本字典"------只要都只看不改就共用一本;谁要在上面做笔记了,才复印一份给他自己用,省纸。
使用场景 :理解进程概念后才能用 ps、top、kill 等命令进行日常运维------查看谁占了 CPU、谁在吃内存、谁僵死了需要清理。
3.2 睡眠状态(与信号相关)
| 状态 | 英文 | 含义 |
|---|---|---|
| 不可中断睡眠 | Uninterruptible sleep (D) | 通常在内核态等待 I/O,不响应信号,连 kill -9 也杀不掉 |
| 可中断睡眠 | Interruptible sleep (S) | 等待事件(如键盘、网络),可被信号唤醒 |
生活例子:
- D(不可中断):像"银行柜员正在数钱"------数到一半不能被打断,否则会乱,你只能等他数完。
- S(可中断):像"你在家等快递"------在睡觉,但手机一响(信号来了)你就醒了去开门。
使用场景 :看到 ps aux 里大量 D 状态进程,通常说明 I/O 有瓶颈(磁盘慢、NFS 挂了等),需要排查存储或网络。
3.3 优先级
- 0--99:内核使用的实时优先级,用户一般不改。
- 100--139 :用户可调范围;其中 nice 值 (NI)影响调度,用户可用
nice/renice调整。
生活例子:优先级像"医院排队"------急诊(高优先级 0-99)直接进去,普通门诊(100-139)按号排队;nice 值像"主动让号"------你可以对系统说"我这个任务不急,让别人先",就是把 nice 调高(数字越大越"礼让",优先级越低)。
使用场景 :大文件压缩(tar/gzip)很耗 CPU,但不急------用 nice -n 19 以最低优先级运行,不影响线上业务。数据库备份、日志轮转等后台任务也常用 nice 降低优先级。
四、Linux 进程管理之二:查看与管理进程 📊
4.1 进程状态码(ps STAT)一览
| 状态 | 含义 |
|---|---|
| D | 不可中断睡眠(通常为 I/O),无法用信号杀死 |
| R | 运行或就绪(在运行队列中) |
| S | 可中断睡眠 |
| T | 停止(如 Ctrl+Z 或 SIGSTOP) |
| Z | 僵死(已退出,父进程未 wait) |
| < | 高优先级 |
| N | 低优先级 |
| + | 前台进程组 |
| l | 多线程 |
| s | 会话首进程 |
生活例子:R 像"正在干活";S 像"在等别人回消息,随时可被叫醒";D 像"正在搬一块不能松手的大石头",不能被打断;T 像"被按了暂停";Z 像"人已走、桌子还没收"的僵尸位。
🏃 R 运行/就绪
😴 S 可中断睡眠
🔒 D 不可中断睡眠
⏸️ T 停止
💀 Z 僵死
4.2 时间复杂度简述(原稿摘录)
- O(1)、O(n)、O(log n)、O(n²)、O(2^n) 等表示算法/调度复杂度,用于理解内核调度与数据结构设计,此处不展开。
4.3 init 与 pstree
- init:进程号为 1,所有用户进程的祖先。
- pstree:以树状图显示进程父子/派生关系。
4.4 ps:进程状态
- ps = Process State,报告当前进程状态。
- SysV 风格 :选项以
-开头(如ps -ef)。 - BSD 风格 :选项可无
-(如ps aux)。
生活例子 :
ps像"拍一张教室的照片"------这一瞬间谁在、谁在干什么、谁在打瞌睡,一目了然。
使用场景:
ps aux | grep java:Java 应用上线后确认进程是否在跑。ps -ef | grep defunct:查看有没有僵尸进程需要清理。ps -eo pid,ppid,%cpu,%mem,cmd --sort=-%cpu | head:找出 CPU 占用最高的前几个进程(排障利器)。
常用组合:
| 选项 | 含义 |
|---|---|
| a | 与终端相关的进程 |
| u | 以用户为主显示 |
| x | 与终端无关的进程(如守护进程) |
ps aux | head 示例列说明:
| 列 | 含义 |
|---|---|
| USER | 进程所属用户 |
| PID | 进程 ID |
| %CPU | CPU 占用百分比 |
| %MEM | 内存占用百分比 |
| VSZ | 虚拟内存大小 |
| RSS | 常驻内存 |
| TTY | 关联终端,? 表示无终端 |
| STAT | 状态(见上表) |
| START | 启动时间 |
| TIME | 占用 CPU 时间 |
| COMMAND | 命令;带 [] 的常为内核线程 |
ps 命令简介(原稿):用于报告当前系统进程状态,可配合 kill 中断或删除程序;可查看进程是否在运行、是否僵死、资源占用等。
常用用法:
bash
ps -o pid,comm,ni # 自定义列
ps -axo pid,comm,ni # 更详细
ps aux | grep "bash" # 查某进程
ps -el | head 中额外列:PRI(优先级)、NI(nice 值)。ps -eF 等格式中还有 PSR(运行在哪颗 CPU 上)。
4.5 ps 选项汇总(原稿保留)
以下为原稿中的 ps 参数列表,便于查阅,不逐条解释:
- -a:显示所有终端下执行的程序,除阶段作业领导者。
- a:显示当前终端下所有程序,含其他用户。
- -A:显示所有程序。
- -c / c:显示 CLS/PRI 栏位 / 真实指令名。
- -C<指令名>:按指令名列出。
- -d:显示所有程序,但不包括阶段作业领导者。
- -e:同 -A。
- e:显示环境变量。
- -f:显示 UID, PPID, C, STIME 等。
- f:树状结构。
- -g / g:按群组。
- -G<GID>:属于该群组的程序。
- h:不显示标题列。
- -H:树状结构。
- -j / j:工作控制格式。
- -l / l:详细格式。
- L:栏位相关信息。
- -m / m:所有线程。
- n:USER/WCHAN 以数字显示。
- -N:除当前 ps 终端外的所有程序。
- -p<PID>:指定 PID。
- r:仅当前终端正在运行的进程。
- -s<作业>:指定阶段作业。
- s:信号格式。
- S:含已中断子程序。
- -t<终端>:指定终端。
- -T:当前终端下所有程序。
- -u / -U / U:按用户。
- v:虚拟内存格式。
- -V:版本。
- -w / w:宽格式。
- x:不以终端区分。
- -y:配合 -l 时不显示 F,以 RSS 取代 ADDR。
- 以及 --cols 、--forest 、--no-headers 等长选项。
4.6 pstree
- pstree:以树状图显示进程派生关系。
- 常用:
pstree -p(显示 PID)、pstree -a(显示完整命令行,相同进程名可压缩)。
生活例子 :
pstree像"画一棵家族族谱树"------谁是爸爸(父进程)、谁是儿子(子进程),一层层展开看得清楚。
使用场景 :某个进程 CPU 飙高,用 pstree -p PID 看它生出了哪些子进程,定位"到底是哪个子进程在捣乱"。
选项:-a 完整指令;-c 不精简;-G VT100 绘图;-h 高亮当前;-H<PID> 高亮指定进程;-l 长格式;-n 按 PID 排序;-p 显示 PID;-u 显示用户名;-U UTF-8 等。
4.7 pgrep
- pgrep:按名称从运行进程中查找,并输出 PID。
- 例:
pgrep -lo httpd(最小 PID+进程名)、pgrep -ln httpd(最大 PID+进程名)、pgrep -l httpd、pgrep httpd(仅 PID)、pgrep bash、pgrep -u root bash。
生活例子 :
pgrep像"在公司花名册里按名字搜人"------只要说出名字(进程名),就能找到他的工号(PID)。
使用场景 :脚本里判断某服务是否在运行:pgrep nginx > /dev/null && echo "nginx is running" || echo "nginx is down",比 ps aux | grep 简洁得多。
选项:-o 最小 PID;-n 最大 PID;-l 显示进程名;-P 父进程;-g 进程组;-t 终端;-u 有效用户。
4.8 pidof
- pidof <程序名>:根据程序名查进程 PID。
- 例:
pidof nginx、pidof crond、pidof init。 - 选项:-s 仅返回一个;-c 同 root 目录;-x 脚本开启的进程;-o 排除指定 PID。
4.9 top:实时动态查看
- top:实时显示系统整体与进程状态,可交互管理。
- top -d 1 :刷新间隔 1 秒;top -d 1 -b -n 3:批处理模式,刷新 3 次后退出。
生活例子 :
top像"挂在墙上的实时监控大屏"------CPU、内存、进程状态每秒刷新,谁在"闹事"一眼就能看到;你还能当场按k把捣乱的进程"请出去"。
使用场景:
- 线上服务器响应变慢时,第一步往往就是 SSH 上去跑
top,看 CPU 和内存被谁占满了。 top -b -n 1 > /tmp/top_snapshot.txt:批处理模式抓一次快照写入文件,方便事后分析或发给同事。- 按
M按内存排序、按P按 CPU 排序,快速定位资源大户。
界面示例与含义:
top - 09:14:23 up 56 min, 1 user, load average: 0.00, 0.00, 0.00
Tasks: 94 total, 1 running, 93 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.0%us, 0.3%sy, 0.0%ni, 99.7%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 1004352k total, 188692k used, 815660k free, 15364k buffers
Swap: 2031612k total, 0 used, 2031612k free, 57760k cached
| 项 | 含义 |
|---|---|
| up 56 min | 运行时长 |
| load average | 1/5/15 分钟平均负载(见下节) |
| Tasks | 总进程数,running/sleeping/stopped/zombie |
| Cpu(s) us/sy/ni/id/wa/hi/si/st | 用户/系统/nice/空闲/等待 I/O/硬中断/软中断/被偷走 |
| Mem/Swap | 内存与交换区使用情况 |
| 进程列表 VIRT/RES/SHR/S | 虚拟内存/常驻/共享/状态 |
top 内交互键(原稿):h 帮助;k 终止进程;i 忽略空闲和僵死;q 退出;r 调整优先级;S 累计模式;s 改刷新间隔;f/F 添加/删除列;o/O 排序列;l/m/t 切换负载/内存/CPU;c 完整命令行;M/P/T 按内存/CPU/时间排序;w 保存到 ~/.toprc。
4.10 系统平均负载(load average)
- 定义 :一段时间内,处于可运行 和不可中断睡眠状态的进程数的平均值。
- 三个数:分别对应最近 1 分钟、5 分钟、15 分钟。
- 粗略理解:单核上 load≈1 表示满负荷;多核则要乘以核数(如 4 核,load 约 4 才满负荷)。若长期明显超过 CPU 核数,说明系统繁忙或存在瓶颈。
生活例子:load average 像"收银台前排队人数"------1 个收银员时排队 1 人算满负荷;4 个收银员时排队 4 人才算满负荷;排队远超过收银员数说明系统"堵"了。
4.11 进程间通信(IPC)与信号
- IPC:Inter Process Communication,方式包括共享内存、信号(Signal)、信号量(Semaphore)等。
- kill -l:列出所有可用信号。
生活例子:
- 共享内存:像"办公室白板"------多个人都能在上面写和读。
- 信号:像"拍肩膀"------轻拍一下(SIGTERM)表示"请你下班";使劲推(SIGKILL)表示"保安把你请走"。
- 信号量:像"会议室门口的牌子"------翻到"使用中"别人就等着,翻到"空闲"才能进去。
常用信号:
| 编号 | 名称 | 含义 |
|---|---|---|
| 1 | SIGHUP | 挂起,常用来让进程重读配置文件 |
| 2 | SIGINT | 中断(如 Ctrl+C) |
| 9 | SIGKILL | 强制终止,不可捕获 |
| 15 | SIGTERM | 终止(默认),建议先用于优雅退出 |
📌 进程
SIGTERM(15)
👋 优雅终止
SIGKILL(9)
⚡ 强制杀死
✅ 正常清理退出
❌ 立即终止
4.12 kill 命令
- 作用:向指定 PID 或作业发送信号;默认发送 SIGTERM(15)。
- 语法 :
kill [-s 信号] PID或kill -信号编号 PID。 - 例 :
kill -l列出信号;ps -ef | grep vim查 PID 后kill 3268或kill -9 3268。
只有 SIGKILL(9) 可无条件终止进程,其他信号进程可忽略。
生活例子 :
kill像"给某个人发消息"------默认发"请你走吧"(SIGTERM 15),对方可能会先收拾好东西再走;如果他不走,你就发"保安!"(SIGKILL 9),直接强制带走。
使用场景:
- Nginx 平滑重启:
kill -HUP $(pidof nginx)让 Nginx 重读配置文件,不中断正在服务的请求。 - 应用卡死:先
kill PID(SIGTERM),等几秒不退再kill -9 PID(SIGKILL)。 - 批量杀进程:
kill $(pgrep -u testuser)杀掉某用户的所有进程。
4.13 nice 与 renice
- nice :以指定优先级启动程序:
nice -n NI COMMAND。- 例:
nice -n 19 tar zcf pack.tar.gz documents(低优先级打包);nice -n -20表示高优先级(需权限)。
- 例:
- renice :调整已运行进程的 nice 值:
renice -n NI -p PID,如renice -3 3704。
4.14 前台与后台作业
| 概念 | 说明 |
|---|---|
| 前台作业 | 占用当前终端,阻塞输入直到结束 |
| 后台作业 | 不占用提示符,在后台运行 |
- COMMAND &:在后台启动命令。
- Ctrl+Z:把当前前台作业挂起并放到后台(默认处于 stopped 状态)。
例 :tar -jcf /tmp/etc.tar.bz2 /etc/* & 后台压缩;ps aux | grep tar 查看。
4.15 jobs、bg、fg
| 命令/按键 | 作用 |
|---|---|
| jobs | 列出当前 shell 的后台/挂起任务及任务号 |
| jobs -l | 同时显示 PID |
| bg [任务号] | 让挂起的作业在后台继续运行,如 bg 1 |
| fg [任务号] | 把后台或挂起的作业调到前台,如 fg 1 |
| Ctrl+Z | 前台作业挂起并送入后台(stopped) |
kill 作业 :kill %JOBID 可终止指定作业(JOBID 为 jobs 显示的任务编号)。
生活例子:前台作业像"你正在做的题",后台作业像"洗衣机在洗衣服";Ctrl+Z 是把当前题目先放一边,bg 是让放一边的任务在后台继续跑,fg 是把它重新拿到面前做。
Ctrl+Z
bg
fg
COMMAND &
🖥️ 前台运行
⏸️ 后台 Stopped
🔄 后台 Running
4.16 vmstat:系统状态
- vmstat [间隔秒数] [次数] :如
vmstat 1每秒刷新;vmstat 1 5每秒一次共 5 次。
生活例子 :
vmstat像"汽车仪表盘"------转速(CPU)、油量(内存)、发动机温度(I/O)、排队车辆(进程队列)全在一个面板上看,每隔几秒刷新一次。
使用场景:
- 线上数据库慢了,
vmstat 1看wa(等待 I/O)是否高------高说明磁盘是瓶颈。 - 看
si/so是否频繁------频繁说明内存不够、不断在用交换分区。 - 看
r列------运行队列长度持续远超 CPU 核数,说明 CPU 负载过高。
列简要含义:
| 类别 | 列 | 含义 |
|---|---|---|
| procs | r | 运行队列长度 |
| procs | b | 阻塞队列长度 |
| memory | swpd/free/buff/cache | 交换/空闲/缓冲/缓存 |
| swap | si/so | 换入/换出 |
| io | bi/bo | 块读入/写出 |
| system | in/cs | 中断次数/上下文切换 |
| cpu | us/sy/id/wa/st | 用户/系统/空闲/等待 I/O/被偷走 |
4.17 uptime
- uptime:显示当前时间、运行时长、登录用户数、load average(1/5/15 分钟)。
- 例:
15:31:30 up 127 days, 3:00, 1 user, load average: 0.00, 0.00, 0.00。 - 系统平均负载可理解为"排队等 CPU(及等 I/O)的进程多少";单核下若每个核负载持续 >3~5 需关注。
4.18 常用 proc 与 CPU/内存信息
uptime 使用场景补充:
- SSH 上服务器后第一条命令往往就是
uptime------一眼看到机器跑了多久(有没有意外重启过)和当前负载。- 比较 1 分钟和 15 分钟的负载:如果 1 分钟远高于 15 分钟,说明刚刚开始忙 ;反之如果 15 分钟高而 1 分钟低,说明之前忙、现在恢复了。
- /proc/meminfo:内存信息。
- /proc/cpuinfo:CPU 信息。
生活例子 :
/proc目录像"体检报告文件夹"------/proc/cpuinfo是 CPU 的体检单(型号、核数、频率),/proc/meminfo是内存的体检单(总量、已用、缓存)。
使用场景:
cat /proc/cpuinfo | grep "model name"查看 CPU 型号------买了云服务器先看看配置对不对。cat /proc/meminfo | head查看总内存和可用内存------判断需不需要加内存。- 脚本中自动获取 CPU 核数:
CORES=$(grep -c ^processor /proc/cpuinfo),用来决定并发线程数。
小结
- 循环:while/until/for 与 break/continue 是脚本里重复逻辑与提前退出的基础。
- 函数:封装逻辑、传参、return 状态码,便于复用与结构化。
- 进程:通过 ps/top/pstree 查看状态与树形关系;用信号(尤其是 SIGTERM/SIGKILL)和 nice/renice 管理行为与优先级;用 jobs/bg/fg 管理前后台作业。
- 负载与性能:uptime 的 load average 与 vmstat 的 r/b/cpu 等结合看,可初步判断系统是否过载或 I/O 是否紧张。