本文目录导读:

在Shell脚本中实现信号量同步,可以通过以下几种方式:
使用文件作为信号量
基础示例
#!/bin/bash
# 信号量文件
SEM_FILE="/tmp/my_semaphore"
# 获取信号量
acquire() {
while ! mkdir "$SEM_FILE" 2>/dev/null; do
sleep 1
done
}
# 释放信号量
release() {
rmdir "$SEM_FILE" 2>/dev/null
}
# 使用示例
echo "尝试获取信号量..."
acquire
echo "获取到信号量,执行临界区代码"
sleep 3 # 模拟临界区操作
release
echo "释放信号量"
使用flock进行文件锁
写锁示例
#!/bin/bash
# 使用flock实现信号量
LOCK_FILE="/tmp/myapp.lock"
# 获取排他锁(等待其他进程释放)
(
flock -x -w 10 200 || exit 1 # 等待10秒
echo "进程 $$ 获取到锁"
# 临界区代码
sleep 5
echo "进程 $$ 释放锁"
) 200>$LOCK_FILE
# 非阻塞尝试获取锁
(
flock -x -n 200 || {
echo "无法获取锁,进程退出"
exit 1
}
echo "进程 $$ 获取到锁"
sleep 3
echo "释放锁"
) 200>$LOCK_FILE
共享锁示例(多个读取者)
#!/bin/bash
LOCK_FILE="/tmp/shared_resource.lock"
# 获取共享锁(读锁)
function acquire_shared_lock() {
(
flock -s -w 5 200 || {
echo "无法获取共享锁"
exit 1
}
echo "获取共享锁成功, PID: $$"
# 读取操作
cat "$LOCK_FILE" 2>/dev/null
) 200>$LOCK_FILE
}
# 获取排他锁(写锁)
function acquire_exclusive_lock() {
(
flock -x -w 5 200 || {
echo "无法获取排他锁"
exit 1
}
echo "获取排他锁成功, PID: $$"
# 写入操作
echo "Updated by PID $$" > "$LOCK_FILE"
) 200>$LOCK_FILE
}
# 清理锁
function release_lock() {
flock -u 200 2>/dev/null
}
使用mkfifo命名管道
基于管道的信号量
#!/bin/bash
SEM_NAME="/tmp/my_semaphore_fifo"
# 初始化信号量
if [ ! -p "$SEM_NAME" ]; then
mkfifo "$SEM_NAME"
# 初始计数(示例:3个可用资源)
for i in {1..3}; do
echo "token" > "$SEM_NAME" &
done
fi
# P操作(获取信号量)
P() {
read token < "$SEM_NAME"
echo "获取资源: $token"
}
# V操作(释放信号量)
V() {
echo "token" > "$SEM_NAME" &
echo "释放资源"
}
# 使用示例
echo "尝试获取资源..."
P
echo "使用资源中..."
sleep 2
V
使用System V信号量 (需要ipcs命令)
IPC信号量实现
#!/bin/bash
# 创建信号量
create_semaphore() {
local key=$1
local count=$2
# 使用ipcrm清理旧的信号量
ipcrm -S "$key" 2>/dev/null
# 创建新的信号量集
ipcs -Q --create "$key" "$count" 2>/dev/null || {
# 使用semtool或自定义工具
echo "Creating semaphore with key: $key, count: $count"
}
}
# 信号量操作函数
sem_wait() {
local sem_id=$1
# 使用ipcrm或自定义脚本操作信号量
# 这里简化实现
while true; do
# 尝试信号量操作
if ipcs -i "$sem_id" -O 2>/dev/null; then
break
fi
sleep 1
done
}
sem_signal() {
local sem_id=$1
# 释放信号量
ipcs -i "$sem_id" -o 2>/dev/null
}
使用命名管道实现读者-写者问题
读者优先实现
#!/bin/bash
#!/bin/bash
READER_FIFO="/tmp/reader_fifo"
WRITER_FIFO="/tmp/writer_fifo"
READ_COUNT=0
READ_COUNT_LOCK="/tmp/read_count.lock"
# 初始化
init_fifos() {
[ -p "$READER_FIFO" ] || mkfifo "$READER_FIFO"
[ -p "$WRITER_FIFO" ] || mkfifo "$WRITER_FIFO"
}
# 读者获取读权限
reader_acquire() {
(
flock -x 200
READ_COUNT=$((READ_COUNT + 1))
if [ "$READ_COUNT" -eq 1 ]; then
# 第一个读者阻止写者
echo "block" > "$WRITER_FIFO"
fi
) 200>$READ_COUNT_LOCK
}
# 读者释放读权限
reader_release() {
(
flock -x 200
READ_COUNT=$((READ_COUNT - 1))
if [ "$READ_COUNT" -eq 0 ]; then
# 最后一个读者释放写者
echo "unblock" < "$WRITER_FIFO" &
fi
) 200>$READ_COUNT_LOCK
}
# 写者获取写权限
writer_acquire() {
echo "wait" > "$WRITER_FIFO"
read response < "$WRITER_FIFO"
}
# 写者释放写权限
writer_release() {
echo "done" > "$WRITER_FIFO"
}
使用临时文件实现计数信号量
计数信号量实现
#!/bin/bash
SEM_DIR="/tmp/counting_sem"
SEM_COUNT=3 # 最大并发数
# 初始化
init_semaphore() {
mkdir -p "$SEM_DIR"
# 创建初始令牌
for i in $(seq 1 $SEM_COUNT); do
touch "$SEM_DIR/token_$i"
done
}
# P操作 - 获取一个令牌
P() {
while true; do
# 查找并获取一个令牌
for token in "$SEM_DIR"/token_*; do
if [ -f "$token" ]; then
if rm "$token" 2>/dev/null; then
echo "获取令牌: $token"
return 0
fi
fi
done
sleep 1
done
}
# V操作 - 释放一个令牌
V() {
local token="$SEM_DIR/token_$$_$(date +%s%N)"
touch "$token"
echo "释放令牌"
}
# 清理
cleanup() {
rm -rf "$SEM_DIR"
}
# 使用示例
init_semaphore
# 启动多个并发进程
for i in {1..5}; do
(
echo "进程 $$ 等待资源"
P
echo "进程 $$ 获取资源,处理中..."
sleep $((RANDOM % 3 + 1))
V
echo "进程 $$ 完成"
) &
done
wait
cleanup
高级封装示例
完整的信号量库
#!/bin/bash
# semaphore.sh - 信号量库
# 配置
SEM_BASE_DIR="/dev/shm/semaphores"
# 初始化信号量
sem_init() {
local name=$1
local count=${2:-1}
local dir="$SEM_BASE_DIR/$name"
mkdir -p "$dir"
echo "$count" > "$dir/max"
echo "0" > "$dir/current"
# 创建初始令牌
for i in $(seq 1 $count); do
touch "$dir/token_$i"
done
}
# 获取信号量
sem_wait() {
local name=$1
local timeout=${2:-0}
local dir="$SEM_BASE_DIR/$name"
local start_time=$(date +%s)
while true; do
for token in "$dir"/token_*; do
if [ -f "$token" ] && rm "$token" 2>/dev/null; then
local current=$(< "$dir/current")
echo $((current + 1)) > "$dir/current"
return 0
fi
done
# 检查超时
if [ "$timeout" -gt 0 ]; then
local elapsed=$(( $(date +%s) - start_time ))
if [ "$elapsed" -ge "$timeout" ]; then
echo "Timeout waiting for semaphore: $name"
return 1
fi
fi
sleep 0.1
done
}
# 释放信号量
sem_post() {
local name=$1
local dir="$SEM_BASE_DIR/$name"
local token="$dir/token_$$_$(date +%s%N)"
touch "$token"
local current=$(< "$dir/current")
echo $((current - 1)) > "$dir/current"
}
# 销毁信号量
sem_destroy() {
local name=$1
local dir="$SEM_BASE_DIR/$name"
rm -rf "$dir"
}
# 信号量状态
sem_status() {
local name=$1
local dir="$SEM_BASE_DIR/$name"
if [ -d "$dir" ]; then
local max=$(< "$dir/max")
local current=$(< "$dir/current")
local available=$(ls "$dir" | grep -c "token_" 2>/dev/null)
echo "Semaphore '$name':"
echo " Max: $max"
echo " In Use: $current"
echo " Available: $available"
else
echo "Semaphore '$name' not found"
fi
}
# 使用示例
if [ "$1" = "test" ]; then
# 创建2个资源的信号量
sem_init "test_sem" 2
# 启动3个并发任务
for i in {1..3}; do
(
echo "Task $i: Waiting for resource..."
if sem_wait "test_sem" 5; then
echo "Task $i: Got resource, working..."
sleep 2
echo "Task $i: Done"
sem_post "test_sem"
else
echo "Task $i: Failed to get resource"
fi
) &
done
wait
sem_status "test_sem"
sem_destroy "test_sem"
fi
使用建议
-
选择合适的方法:
flock:简单可靠,推荐用于大多数场景- 文件锁:最通用,但注意竞态条件
- 命名管道:适合进程间通信
-
错误处理:
- 添加超时机制
- 处理信号中断
- 确保锁的释放
-
性能考虑:
- 避免忙等待,使用延迟重试
- 使用内存文件系统(/dev/shm)提高性能
-
调试技巧:
# 查看当前锁 lslocks # 或使用脚本调试 set -x
选择合适的同步机制取决于具体需求,flock 通常是实现Shell脚本信号量同步的最简单可靠方式。
版权声明: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直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播