目录
题目:
有界缓冲区问题,使用Bash Scripting 设计脚本,利用信号量技术处理生产者-消费者的任务,通过 两个竞争性进程共享一个逻辑资源。在这个任务中,这个共享资源是一个字符(a-z 或A-Z)缓冲区。我们的连续可重用资源的内容将存储在一个名为 buffer.txt 的文件 中,一次只能由一个进程使用(mutual exclusion)。Producer的任务是生成数据单元 以便将它们放入缓冲区;相反,Consumer 的任务是从缓冲区中删除数据单元(在此任务中是字符)
设计思路:
对于生产者消费者问题,首先需要一个生产者脚本producer.sh和消费者脚本consumer.sh,生产者生产共享资源到文件buffer.txt中,消费者从buffer.txt中读取并消费资源,通过信号量来同步两个进程,确保他们不会同时访问buffer.txt,而对于信号量的操作还需要wait.sh和signal.sh脚本,除此之外还需要一个初始化脚本init.sh来进行初始化信号量,初始化缓冲区文件,运行生产者和消费者脚本等操作。
代码实现:
init.sh
#!/bin/bash
chmod +x producer.sh consumer.sh wait.sh signal.sh
# 初始化信号量
echo "1" > free_semaphore.txt
echo "10" > space_semaphore.txt
echo "0" > data_semaphore.txt
# 创建/初始化缓冲区文件
echo "" > buffer.txt
# 在后台运行生产者和消费者脚本
./producer.sh &
./consumer.sh &
producer.sh
#!/bin/bash
while true; do
# 等待缓冲区中有可用空间
./wait.sh space_semaphore.txt
# 生产数据(生成一个随机字符)
data=$(head /dev/urandom | tr -dc 'a-zA-Z' | head -c 1)
# 获取缓冲区锁
./wait.sh free_semaphore.txt
# 将数据追加到缓冲区
echo $data >> buffer.txt
echo "生产:$data"
# 释放缓冲区锁
./signal.sh free_semaphore.txt
# 增加数据计数
./signal.sh data_semaphore.txt
done
consumer.sh
#!/bin/bash
while true; do
# 等待缓冲区中有可用数据
./wait.sh data_semaphore.txt
# 获取缓冲区锁
./wait.sh free_semaphore.txt
# 消费数据(从缓冲区中读取并删除第一个字符)
data=$(tail -n 1 buffer.txt)
echo "消费:$data"
sed -i '$d' buffer.txt
# 释放缓冲区锁
./signal.sh free_semaphore.txt
# 减少数据计数
./signal.sh space_semaphore.txt
done
signal.sh
#!/bin/bash
file=$1
# 生成唯一的锁文件名,基于文件名
lockfile="${file}.lock"
# 将锁文件名的字符串形式转换为文件描述符
exec {unique_fd}>"$lockfile"
# 使用 flock 来获取锁
flock -x $unique_fd
echo $(( $(cat $file) + 1 )) > $file
flock -u $unique_fd
wait.sh
#!/bin/bash
file=$1
# 生成唯一的锁文件名,基于文件名
lockfile="${file}.lock"
# 将锁文件名的字符串形式转换为文件描述符
exec {unique_fd}>"$lockfile"
while [ $(cat $file) -le 0 ]; do
sleep 1
done
flock -x $unique_fd
echo $(( $(cat $file) - 1 )) > $file
flock -u $unique_fd
kill.sh
pkill -f './consumer.sh'
pkill -f './producer.sh'
pkill -f './wait.sh'
pkill -f './signal.sh'
rm -f buffer.txt file.lock
rm -f data_semaphore.txt data_semaphore.txt.lock
rm -f free_semaphore.txt free_semaphore.txt.lock
详细思路:
首先是init.sh,初始化信号操作
free_semaphore.txt是互斥信号量,确保生产者和消费者同时只能有一个进程访问buffer,space_semaphore.txt表示buffer的当前容量,data_semaphore.txt,表示当前buffer中的数据个数。生产者每生产一个资源到buffer,data_semaphore.txt就会加一,space_semaphore.txt减一,当space_semaphore.txt为0时,表示当前buffer已满,那么生产者就无法再生产,消费者同理。
然后是初始化缓冲区文件
如果没有buffer.txt文件则会先创建。
然后是运行生产者和消费者脚本
为了方便一点还在前面加上了
接下来是生产者和消费者
由上面伪代码我们可以看出,生产者和消费者中需要用到wait和signal对信号量进行操作,所以我们先完成wait和signal
对于wait.sh和signal.sh我们要保证它的原子性,这里使用文件锁flock来实现
wait.sh
signal.sh
这里以信号量文件名来生成锁文件名,同时以锁文件名来生成文件描述符,这可以保证不同进程(wait.sh和signal.sh)对同一个信号量的更改是互斥的。也就是说,同一时刻只能有一个wait.sh的进程或signal.sh的进程对一个信号量进行更改。
wait.sh中如果传入的信号量小于0,就会被sleep,(注意这个对信号量的判定语句要放在flock之前,如果放在flock之后就会产生死锁。)如果信号量大于0,就将其减一。
Signal.sh将传入的信号量加一。
然后回去写生产者和消费者脚本
producer.sh
consumer.sh
生产者生产数据
从系统中的/dev/urandom设备中读取数据,并删除非字母字符,保留字符字母集,并且从处理过的字符字母集中取前一个字符。
获取到缓冲区锁之后将数据添加到buffer
这里将新的数据也就是字符以整行的形式添加到buffer,与后面消费者以整行形式删除相呼应。
之后释放缓冲区锁,并且将信号量data加一,表示buffer中的数据加一。
之后是消费者,先进入缓冲区,然后消费资源
然后释放缓冲区锁,再将sapce信号量加一,表示buffer中可用空间加一。
最后kill.sh来结束进程