Bash 循环与函数、Linux 进程管理

目录

  1. [一、Bash 脚本编程之九:while 循环与循环控制](#一、Bash 脚本编程之九:while 循环与循环控制)
  2. [二、Bash 脚本编程之十:函数](#二、Bash 脚本编程之十:函数)
  3. [三、Linux 进程管理之一:进程概念](#三、Linux 进程管理之一:进程概念)
  4. [四、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

  1. -i 指定网卡;若不存在则退出。
  2. 若网卡存在,让用户输入别名(可为空);若别名不空则需确保事先不存在,否则报错并重新输入。
  3. 别名正确后,让用户输入地址和掩码,并配置到该别名上。
  4. 若使用 -v,配置完成后显示配置结果;否则不显示。

(脚本具体实现略,保留原题要求。)

相关练习(原稿)

  1. 将系统安装光盘挂载至 /media/yum 目录,用其实现 yum 仓库。
  2. 配置使用 http://172.16.0.1/yum/{Server,VT,Cluster,ClusterStorage} 为可用 yum 仓库。

1.5 练习:同一 repo 文件中创建多个 Yum 源

说明:在同一 repo 文件中创建多个 Yum 源指向。

  1. 接受一个文件名作为参数,文件放在 /etc/yum.repos.d/,且以 .repo 结尾;若文件已存在则报错退出。
  2. 提示用户输入 repo id;若为 quit 则退出;否则继续。
  3. 输入 repo name 和 baseurl,按 repo 文件格式写入该文件。
  4. enabled 默认为 1,gpgcheck 默认为 0。
  5. 循环执行直到用户输入 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

要求:

  1. 提示用户输入一个用户名。
  2. 显示菜单:U|u show UID;G|g show GID;S|s show SHELL;Q|q quit。
  3. 根据用户选择显示对应内容;非上述选项则提示错误并重新选择。

生活例子:菜单脚本就像"自助点餐机"------屏幕上列出选项,你按一个字母,机器就给你对应的东西;按了不认识的键,机器提醒你重新选。

使用场景

  • 运维工具箱:一个脚本入口,可以选"查磁盘""查内存""重启服务""查日志"等常用操作。
  • 新手友好脚本:不需要记命令参数,靠菜单交互即可完成操作。

(实现思路:read 用户名,case 判断选项,用 idgrep /etc/passwd 取 UID/GID/SHELL。)


1.8 语法检查脚本:有错误则提示用 vim 编辑

要求:

  1. 判断指定 bash 脚本是否有语法错误;若有,提示用户输入 Q 或 q 忽略并退出,其他键则用 vim 打开该脚本。
  2. 若用户保存退出后仍有语法错误,重复步骤 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 在线主机

要求:

  1. 用函数实现"对一台主机是否在线"的判定。
  2. 在主程序中调用该函数,判定范围内所有主机。

写法一:函数内循环 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

要求:

  1. 函数接受一个参数(用户名),判断用户是否存在。
  2. 若存在:显示该用户的 shell 和 UID,并返回正常状态;若不存在:提示并返回错误状态。
  3. 主程序调用该函数。
  4. 扩展 1:主程序让用户输入用户名,再传给函数。
  5. 扩展 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:像"合租房里两个人共用一本字典"------只要都只看不改就共用一本;谁要在上面做笔记了,才复印一份给他自己用,省纸。

使用场景 :理解进程概念后才能用 pstopkill 等命令进行日常运维------查看谁占了 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 httpdpgrep httpd(仅 PID)、pgrep bashpgrep -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 nginxpidof crondpidof 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 信号] PIDkill -信号编号 PID
  • kill -l 列出信号;ps -ef | grep vim 查 PID 后 kill 3268kill -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 1wa(等待 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 是否紧张。
相关推荐
Volunteer Technology2 小时前
LangGraph的WorkFlow(二)
linux·windows·python
Maynor9962 小时前
OpenClaw 中转站配置完全指南
linux·运维·服务器·人工智能·飞书
Tinyundg2 小时前
Linux中的文件权限
linux·运维·服务器
FoldWinCard2 小时前
Python 第五次作业
linux·windows·python
_OP_CHEN2 小时前
【Linux系统编程】(三十三)System V 共享内存精讲:Linux 最快 IPC 的原理与实战精髓
linux·服务器·操作系统·共享内存·c/c++·ipc·system v
dingdingfish3 小时前
Bash学习 - 第6章:Bash Features,第3节:Interactive Shells
bash·shell·interactive
RisunJan3 小时前
Linux命令-lspci(显示当前主机的所有PCI总线信息)
linux·运维·服务器
kaka__553 小时前
cma内存申请页迁移流程浅析
linux
未既3 小时前
linux以及docker修改文件描述符
linux·运维·docker