本文目录导读:

- 使用
mkdir原子性操作(推荐) - 使用
flock命令(Linux专用) - 使用
lockfile命令(procmail包) - 使用 PID 文件(简单但不够安全)
- 高级用法:可重入锁(单进程多实例)
- 实用的封装函数
- 选择建议
- 关键注意事项
在Shell脚本中实现互斥锁主要有以下几种方法:
使用 mkdir 原子性操作(推荐)
mkdir 是原子操作,这是最常用且可移植性最好的方法:
#!/bin/bash
LOCK_DIR="/tmp/mylock.$$"
LOCK_NAME="my_script_lock"
# 尝试获取锁
if ! mkdir "$LOCK_DIR/$LOCK_NAME" 2>/dev/null; then
echo "无法获取锁,脚本已在运行中"
exit 1
fi
# 确保在脚本退出时释放锁
trap 'rm -rf "$LOCK_DIR/$LOCK_NAME"' EXIT
# 临界区代码
echo "执行需要互斥的操作..."
sleep 10
# 锁会在脚本退出时自动释放
使用 flock 命令(Linux专用)
flock 是Linux提供的文件锁工具,更加灵活:
#!/bin/bash
LOCK_FILE="/tmp/my_script.lock"
# 尝试获取锁,不会阻塞
exec 200>"$LOCK_FILE"
if ! flock -n 200; then
echo "无法获取锁,脚本已在运行中"
exit 1
fi
# 自动释放锁(文件描述符关闭时)
trap 'rm -f "$LOCK_FILE"' EXIT
# 临界区代码
echo "执行需要互斥的操作..."
sleep 10
flock 的变体用法:
# 阻塞等待锁(直到获取到锁)
flock 200 || exit 1
# 设置超时(等待10秒)
flock -w 10 200 || { echo "等待锁超时"; exit 1; }
使用 lockfile 命令(procmail包)
#!/bin/bash
LOCK_FILE="/tmp/my_script.lock"
# 尝试创建锁文件(创建失败表示有锁)
if ! lockfile -r 0 "$LOCK_FILE"; then
echo "无法获取锁,脚本已在运行中"
exit 1
fi
# 清理锁
trap 'rm -f "$LOCK_FILE"' EXIT
# 临界区代码
echo "执行需要互斥的操作..."
使用 PID 文件(简单但不够安全)
#!/bin/bash
LOCK_FILE="/var/run/my_script.pid"
# 检查锁文件
if [ -f "$LOCK_FILE" ]; then
PID=$(cat "$LOCK_FILE")
if kill -0 "$PID" 2>/dev/null; then
echo "脚本已在运行中 (PID: $PID)"
exit 1
fi
echo "发现过期的锁文件,清理..."
rm -f "$LOCK_FILE"
fi
# 创建锁文件
echo $$ > "$LOCK_FILE"
trap 'rm -f "$LOCK_FILE"' EXIT
# 临界区代码
echo "执行需要互斥的操作..."
高级用法:可重入锁(单进程多实例)
#!/bin/bash
LOCK_DIR="/tmp/myapp.lock"
LOCK_NAME="resource.lock"
LOCK_COUNTER="${LOCK_DIR}/${LOCK_NAME}.counter"
init_lock() {
mkdir -p "$LOCK_DIR"
}
# 可重入锁(同一个进程可以多次获取)
acquire_lock() {
local pid=$$
# 检查是否已经持有锁
if [ -f "$LOCK_COUNTER" ] && [ "$(cat "$LOCK_COUNTER")" = "$pid" ]; then
# 同一个进程,增加计数
local count=$(cat "${LOCK_DIR}/${LOCK_NAME}.count" 2>/dev/null || echo 0)
echo $((count + 1)) > "${LOCK_DIR}/${LOCK_NAME}.count"
return 0
fi
# 尝试获取锁
if mkdir "$LOCK_DIR/$LOCK_NAME" 2>/dev/null; then
echo "$pid" > "$LOCK_COUNTER"
echo 1 > "${LOCK_DIR}/${LOCK_NAME}.count"
return 0
fi
return 1
}
release_lock() {
local count=$(cat "${LOCK_DIR}/${LOCK_NAME}.count" 2>/dev/null || echo 0)
if [ "$count" -le 1 ]; then
# 完全释放锁
rm -rf "$LOCK_DIR/$LOCK_NAME"
rm -f "$LOCK_COUNTER"
rm -f "${LOCK_DIR}/${LOCK_NAME}.count"
else
# 减少计数
echo $((count - 1)) > "${LOCK_DIR}/${LOCK_NAME}.count"
fi
}
# 使用示例
init_lock
if acquire_lock; then
echo "获取锁成功"
# 临界区代码
sleep 5
release_lock
else
echo "获取锁失败"
exit 1
fi
实用的封装函数
#!/bin/bash
# 互斥锁工具函数
LOCK_DIR="/tmp/script_locks"
mkdir -p "$LOCK_DIR" 2>/dev/null
# 获取锁
# 参数: $1 锁名称, $2 超时时间(秒,可选)
acquire_lock() {
local lock_name="$1"
local timeout="${2:-0}"
local lock_path="$LOCK_DIR/$lock_name"
local start_time=$(date +%s)
while true; do
if mkdir "$lock_path" 2>/dev/null; then
echo "$$" > "$lock_path/pid"
return 0
fi
# 检查是否过期(可选)
if [ -f "$lock_path/pid" ]; then
local pid=$(cat "$lock_path/pid")
if ! kill -0 "$pid" 2>/dev/null; then
# 过期的锁,清理后重试
rm -rf "$lock_path"
continue
fi
fi
# 检查超时
if [ "$timeout" -gt 0 ]; then
local current_time=$(date +%s)
if [ $((current_time - start_time)) -ge "$timeout" ]; then
return 1
fi
else
# 无超时设置,直接返回失败
return 1
fi
sleep 0.1
done
}
# 释放锁
release_lock() {
local lock_name="$1"
local lock_path="$LOCK_DIR/$lock_name"
rm -rf "$lock_path"
}
# 使用示例
if acquire_lock "my_script" 5; then
# 确保退出时释放锁
trap 'release_lock "my_script"' EXIT
echo "执行互斥操作..."
sleep 10
else
echo "无法获取锁"
exit 1
fi
选择建议
- 推荐使用
mkdir方法 - 简单可靠,可移植性好 - Linux 环境推荐
flock- 功能强大,支持超时和阻塞等待 - 避免使用 PID 文件 - 容易出问题(PID重用等)
- 需要重入锁时使用计数器方案
关键注意事项
- 始终使用
trap确保锁被释放 - 处理脚本被
kill -9等信号时的情况(无法捕获) - 考虑使用锁文件超时机制防止死锁
- 注意文件系统的挂载类型(NFS等分布式文件系统不支持
mkdir原子性)
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播