Shell脚本进程锁机制解析
这段 Shell 脚本主要实现了进程锁机制,用于确保同一时间只有一个实例在运行。让我逐行解读:
代码功能分析
1. 命令行参数解析 (第9-21行)
while getopts "m:o:r:" arg; do
case $arg in
# ... 参数处理逻辑(代码中省略了具体内容)
esac
done
- 使用
getopts解析命令行参数 - 支持三个带参数的选项:
-m、-o、-r - 具体处理逻辑在代码中被省略了
2. 文件锁机制 (第23-34行)
这是代码的核心部分:
LOCK_FILE="/tmp/gen_emmc_img.lock" echo "Waiting for lock for model gen_emmc_img..." exec 200>"$LOCK_FILE" # 将文件描述符200重定向到锁文件 flock 200 # 对文件描述符200加锁 echo "Lock acquired for gen_emmc_img!"
锁机制的工作原理:
- 创建锁文件
/tmp/gen_emmc_img.lock - 使用
flock命令对文件描述符200加锁 - 如果锁已被占用,脚本会阻塞等待直到锁可用
3. 清理和锁释放 (第30-34行)
_cleanup_flock() {
echo "Releasing lock for gen_emmc_img!"
flock -u 200 # 释放文件描述符200的锁
}
trap _cleanup_flock EXIT # 设置陷阱,在脚本退出时自动调用清理函数
设计目的
这种设计主要用于:
- 防止并发冲突:确保同一时间只有一个
gen_emmc_img.sh进程在运行 - 资源保护:在生成 eMMC 镜像时避免多个进程同时操作导致的冲突
- 自动清理:使用
trap确保即使脚本异常退出也能释放锁
典型应用场景
在嵌入式开发或系统镜像生成过程中,这种锁机制很常见,用于保护:
- 对存储设备的独占访问
- 临时文件的创建和使用
- 系统关键资源的访问
这是一个很经典的 Shell 脚本并发控制实现!
好的,我来深度解读第23-34行的文件锁机制。这部分代码确实比较巧妙,让我详细分解:
逐行深度解析
第23行:定义锁文件路径
LOCK_FILE="/tmp/gen_emmc_img.lock"
- 在系统的临时目录创建一个锁文件
- 文件名包含脚本名称,避免与其他脚本冲突
第26行:文件描述符重定向
exec 200>"$LOCK_FILE"
这是关键的一步:
exec命令在当前shell进程中执行重定向200>表示将文件描述符200重定向到锁文件- 如果文件不存在会自动创建,存在则截断
- 文件描述符200是一个任意选择的大于2的数字(避免与stdin=0, stdout=1, stderr=2冲突)
第27行:获取文件锁
flock 200
flock 命令的工作原理:
- 对文件描述符200指向的文件加锁
- 这是** advisory lock**(建议性锁),不是强制性锁
- 如果锁已被其他进程持有,当前进程会阻塞等待
- 锁的释放发生在文件描述符关闭时
第30-33行:清理函数
_cleanup_flock() {
echo "Releasing lock for gen_emmc_img!"
flock -u 200 # 显式释放锁
}
第34行:陷阱设置
trap _cleanup_flock EXIT
trap 的作用:
- 注册一个信号处理程序
EXIT是一个伪信号,在shell退出时触发- 无论脚本正常结束还是异常退出,都会调用
_cleanup_flock
锁机制的工作原理示意图
进程A启动
↓
exec 200>lockfile # 创建文件描述符
↓
flock 200 # 加锁成功,继续执行
↓
[执行关键代码...]
↓
脚本结束 → trap触发 → flock -u 200 → 释放锁
进程B启动(当A持有锁时)
↓
exec 200>lockfile # 创建文件描述符
↓
flock 200 # 阻塞等待...
↓ # 当进程A释放锁后
flock返回 → 获得锁,继续执行
为什么需要这样设计?
1.原子性操作
# 如果没有flock,你可能会这样写(有竞态条件):
if [ ! -f "$LOCK_FILE" ]; then
touch "$LOCK_FILE" # 这里可能有多个进程同时通过检查
# 执行任务...
rm "$LOCK_FILE"
fi
2.自动清理保障
即使脚本被 Ctrl+C 中断、发生错误退出,trap EXIT 也能确保锁被释放。
3.避免僵尸锁
传统方法可能因为进程崩溃而留下锁文件,导致系统永久锁定。这里使用内核管理的文件锁,进程退出后锁自动释放。
实际执行流程示例
# 终端1:运行脚本 $ ./gen_emmc_img.sh Waiting for lock for model gen_emmc_img... Lock acquired for gen_emmc_img! [正在执行...] # 终端2:同时运行另一个实例 $ ./gen_emmc_img.sh Waiting for lock for model gen_emmc_img... # 这里会阻塞等待,直到终端1的脚本结束 # 当终端1脚本结束时: Releasing lock for gen_emmc_img! # 终端2立即获得锁并继续执行
文件锁的优势
- 跨进程同步:不同shell实例之间可以同步
- 自动清理:进程退出时锁自动释放
- 内核级别:比基于文件的标记更可靠
- 可移植性:大多数Unix-like系统都支持
这种设计在需要保证单实例运行的脚本中非常常见,特别是在处理硬件操作(如eMMC烧录)时尤为重要。
到此这篇关于Shell脚本进程锁机制解析的文章就介绍到这了,更多相关Shell 进程锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!


最新评论