Shell编程基础与进程管理

📅 2026/6/27 3:19:14
Shell编程基础与进程管理
写在前面今天学两块内容——Shell编程基础和进程管理。前者是怎么让Linux帮我干活后者是Linux里面的事都是怎么跑的。看起来不相关但其实是一条线你写Shell脚本让系统干活系统靠进程来执行这些活。那就从最简单的表达式开始说。第一部分Shell编程 — 给Linux定个规矩1.1 表达式 — Shell的语法课Shell表达式说白了就三件事算数、判断、比较。运算表达式 — 小学算数$((3 5)) # 输出8 $((10 % 3)) # 输出1取余数没啥好说的跟计算器一样。测试表达式 — 问问题Shell里问问题答案不是对就是错[ 条件 ] 写成真(0) 写成假(非0)坑来了Shell里执行成功返回0失败返回非0。跟绝大多数编程语言正好相反C语言里0是假1是真。Shell里0是真1是假——别记反了。整数比较 — 谁的数更大运算符含义记忆法-eq等于 (equal)首字母e-ne不等于 (not equal)首字母n-gt大于 (greater than)ggreater-ge大于等于gequal-lt小于 (less than)lless-le小于等于lequal[ $age -gt 18 ] echo 成年了1.2 单括号 vs 双括号 — 两个判官的区别Shell里判断条件有两种写法[ ]老祖宗和[[ ]]新一代。跟iPhone一样新的比旧的功能多特性[ ][[ ]]逻辑与-a✅逻辑或-o\|\|✅正则匹配❌~✅通配符匹配❌支持 ✅可读性一般字母命名的运算符更好结论能用[[ ]]就用[[ ]]除非你的脚本要在上古 Shell 上跑。# 判断是不是纯数字 [[ $var ~ ^[0-9]$ ]] echo 是纯数字 ​ # 判断文件是不是jpg [[ $file *.jpg ]] echo 是图片1.3 流程控制 — 让Shell有脑子if 语句 — 做决策if [[ $score -ge 60 ]]; then echo 及格了 elif [[ $score -ge 30 ]]; then echo 还有救 else echo 回家种地 fi注意if后面的; then——分号表示一句话里换行。也可以写成if [[ $score -ge 60 ]] then echo 及格了 ficase 语句 — 做选择题适合那种值就是这几个之一的场景case $1 in start) echo 启动服务 ;; stop) echo 停止服务 ;; restart) echo 重启服务 ;; *) echo 用法: $0 {start|stop|restart} ;; esaccase比if-elif-elif-elif清爽太多看到;;就相当于这个case结束。最后的*)是默认分支类似 switch 里的 default。第二部分进程管理 — Linux的人口管理2.1 进程到底是什么程序是菜谱进程是在做菜的过程。程序菜谱放在磁盘上是一堆静态的指令。当你双击或执行它操作系统把它加载到内存里分配资源、调度执行——这时候它才叫进程。进程由三部分组成┌─────────────────────────────────┐ │ 进程 代码 数据 PCB │ └─────────────────────────────────┘拿做饭来类比代码Text 菜谱上的步骤怎么切菜、什么时候放盐数据Data/Heap/Stack 锅里的食材变量、状态PCBProcess Control Block 你脑袋里记着的做到哪一步了PCB是最关键的部分——它是进程的身份证。操作系统就是靠PCB来管理所有进程的。PCB里记录的东西包括PID身份证号、状态在排队还是在干活、优先级你是VIP还是普通号、CPU寄存器值刚才做到哪一行了。2.2 进程的五种状态 — 一个人的五种状态在Linux里一个进程从生到死会经历这些状态创建 → 就绪 ↔ 运行 ↔ 阻塞 ↓ 终止拿医院挂号看病来理解创建New你到医院前台挂号了护士在电脑里录入你的信息创建PCB给你分配病历本分配资源。就绪Ready你拿号坐着等啥也没干就等大屏幕叫号等待CPU分配。你已经准备好了一切就差CPU了。运行Running大屏幕叫到你你进了诊室见医生获得CPU正在执行。单核CPU同一时间只能有一个进程在跑就像只有一个医生一次只能看一个病人。阻塞Blocked/Waiting医生说你先去拍个CT片子出来再来找我。你出去等片子等待I/O完成这时候医生可以看下一个病人CPU被调度走了。注意你已经拿到CPU了但因为要等某个事件主动放弃了。⚫终止Terminated看完了拿药走人护士注销你的信息回收资源。就绪 vs 阻塞的区别面试高频题就绪万事俱备只差CPU你坐在诊室门口像等号门一开就能进阻塞CPU来了你也干不了你去拍CT了就算医生喊你你也没法看病2.3 进程是怎么生出来的— fork 和 clone这是这块最重要的知识点面试必问。fork() — 影分身之术pid_t pid fork(); if (pid 0) { // 这是子进程 printf(我是儿子我的PID是%d\n, getpid()); } else if (pid 0) { // 这是父进程 printf(我是爸爸我儿子的PID是%d\n, pid); }fork()会创建一个几乎完全一样的子进程子进程复制了父进程的代码段、数据段、堆、栈父进程收到子进程的PID子进程收到0如果fork失败返回 -1但如果每次都完全复制太浪费内存了——父子进程大部分数据是一样的干嘛复制两份这就引出了写时复制COW, Copy-On-Write刚开始父子进程共享同一块物理内存只是各自的虚拟地址映射到同一块物理内存。只有当其中一方试图修改数据时内核才真正复制一份给修改方。这就像你和你爸住一起客厅的电视共用。只有当你和你爸同时想看不同频道的时候才会再买一台电视。clone() — 轻量级分身fork()是复制所有而clone()可以选择性共享共享内存空间 → 线程不共享内存空间 → 普通进程共享文件描述符 → 同一组打开文件本质是在Linux内核里线程和进程底层都是用 task_struct 表示的线程只是共享了大部分资源的进程。你现在常用的pthread_create底层就是调用的clone()。2.4 进程管理命令 — Linux的人口普查工具① 看谁在跑 — ps静态快照ps -ef # 标准格式所有进程 ps aux # 详细格式含CPU/内存占用 ps aux | grep nginx # 只找nginx相关-ef和aux是两种流派用哪个都行习惯哪个用哪个。② 谁生了谁 — pstree家族树pstree # 树状展示父子关系非常直观 pstree -p # 带上PID你会发现systemd是所有进程的老祖宗PID为1所有进程追根溯源都是它生出来的。③ 实时监控 — top动态直播top # 跑起来像Windows的任务管理器进去之后按P— 按CPU排序M— 按内存排序k— 输入PID杀进程q— 退出④ 查内存 — freefree -h # 友好格式显示GB/MB⑤ 看端口 — lsof谁占了我的端口lsof -i :80 # 看谁占用了80端口 lsof -Pti :80 # 只输出PID适合写脚本用这是排查端口冲突的经典三板斧lsof -i :端口→ 拿到PID →kill⑥ 系统整体性能 — vmstatvmstat 1 5 # 每隔1秒刷新一次总共5次输出里重点关注rrun queue排队的进程数持续大于CPU核数说明CPU不够用bblocked被阻塞的进程数过高说明I/O瓶颈si/soswap换入换出非0说明内存紧张2.5 进程控制 — 生杀大权温柔的杀 vs 暴力的杀kill 1234 # 发SIGTERM(15)请求进程自己退出 ← 温柔版 kill -9 1234 # 发SIGKILL(9)强制杀死 ← 暴力版能用kill 1234就别用kill -9。SIGTERM 相当于跟进程说请收拾一下下班SIGKILL 相当于直接断电拔网线。很多程序收到 SIGTERM 会做清理工作保存数据、关闭文件等收到 SIGKILL 啥都来不及做。按名字杀killall nginx # 杀掉所有nginx进程 killall -i nginx # 杀之前问问你防止误杀 ​ pkill -U test # 杀掉用户test的所有进程 pkill -t pts/1 # 杀掉某个终端的所有进程killall和pkill的区别前者按进程名匹配后者更灵活可以按用户、终端等条件。抢CPU — nice/reniceLinux是多任务系统但CPU只有一个单核。那谁先谁后靠优先级优先级范围-20最高优先级 ~ 19最低优先级 默认值0 数字越小越优先# 启动时指定优先级 nice -n 10 ./slow_script.sh # 低优先级跑让出CPU给重要任务 nice -n -5 ./urgent_task.sh # 高优先级跑需要root权限 ​ # 运行时调整优先级 renice -n 5 -p 1234 # 把PID 1234的优先级改成5 renice -n 10 -u test # 把用户test的所有进程优先级改成10类比超市结账nice值-20 ≈ VIP通道优先结账nice值19 ≈ 你拿了一车东西但只有几件让后面拿一瓶水的先结默认0 ≈ 普通排队2.6 前后台任务 — 一个人当两个人用想象你在终端前前台你眼前正在运行的程序占着你的终端后台在幕后默默运行的程序不占用终端操作# 启动后放到后台 tar -czvf backup.tar.gz /data ​ # 查看所有后台任务 jobs jobs -l # 带PID ​ # 调回前台 fg %1 # 把1号后台任务调到前台 ​ # 暂停的前台任务在后台继续跑 bg %1 # 恢复暂停的1号后台任务 ​ # 杀后台任务 kill %1 # 通过任务编号杀典型场景tar打了大包不想干等按CtrlZ暂停bg %1把它放后台继续跑继续敲其他命令想查进度fg %1调回前台查看要点速查卡Shell编程[ 条件 ] — 单括号传统 [[ 条件 ]] — 双括号推荐支持正则 || 整数比较-eq -ne -gt -ge -lt -le 逻辑运算符(与) ||(或) !(非) case语法 case $var in pattern1) cmd1 ;; pattern2) cmd2 ;; *) default ;; esac进程管理进程三要素代码 数据 PCB最重要的身份证 五种状态创建 → 就绪 ↔ 运行 ↔ 阻塞 → 终止 创建方式fork()完全复制COW| clone()选择性共享线程 ​ 常用命令 ps -ef / ps aux # 看进程 pstree -p # 看父子关系 top # 动态监控 lsof -i :端口 # 查端口占用 free -h # 看内存 vmstat 1 5 # 看系统性能 kill -9 PID # 强制杀 nice -n N cmd # 设置优先级启动 jobs / fg / bg # 前后台管理最后说几句今天学的东西有个很清晰的脉络Shell编程→ 是用代码跟Linux对话的方式表达式和流程控制→ 是语法规则进程→ 是Linux执行这些代码的方式fork/clone→ 是进程如何生孩子面试必考ps/top/kill→ 是你在运维中每天都要用的人口管理工具把进程的五种状态流转和fork()的原理理解透这两个是面试最爱问的。其他的命令多用几次就记住了不用硬背。