14.信号量的代码实现
Posted PacosonSWJTU
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了14.信号量的代码实现相关的知识,希望对你有一定的参考价值。
【README】
1.本文内容总结自 B站 《操作系统-哈工大李治军老师》,内容非常棒,墙裂推荐;
2.信号量基础知识,refer2 posts below.
12.进程同步与信号量_PacosonSWJTU的博客-CSDN博客1.本文内容总结自 B站 《操作系统-哈工大李治军老师》,内容非常棒,墙裂推荐;【1】https://blog.csdn.net/PacosonSWJTU/article/details/12553612013.信号量临界区保护_PacosonSWJTU的博客-CSDN博客1.本文内容总结自 B站 《操作系统-哈工大李治军老师》,内容非常棒,墙裂推荐;2.操作系统使用信号量实现进程同步(合作),走走停停,推进多进程合理有序向前执行; 3.靠临界区保护信号量,靠信号量实现进程同步;0)信号量1)问题 图解: 并发问题例子,empty = -1,但有2个生产者进程睡眠,这说明empty信号量的值是错误的;1)竞争条件: 图片解说:错误和调度顺序有关;2)解决竞争条件的方法 上图显示的执行顺序如下:时间进程代码或操作1P1检查并给empty上锁2P1.register=empthttps://blog.csdn.net/PacosonSWJTU/article/details/125542224
【1】信号量回顾
1)信号量定义:
- 一个整型数字;通过对信号量的访问修改,实现多进程同步(合作),有序执行,(或互相等待,交替执行);
2)操作系统内部也是有信号量来实现多进程同步合作
- 如一个进程操作磁盘,磁盘寻道时,该进程阻塞;
- 一旦磁盘准备好数据后,通过中断通知cpu读取数据;则cpu唤醒上述进程读取数据;
3)借助信号量可以完成的工作
- 工作1:借助信号量实现用户态的进程调度,实现上层应用程序间的同步(合作),交替执行;
- 工作2:借助信号量实现内核态的进程调度;
4)信号量数据结构
- 信号量数据结构包括 value值(信号量值), pcb队列(阻塞到信号量的进程队列);
- 因为信号量的值要被所有进程看到,所以信号量在内核态;pcb是存储进程信息的数据结构;
【2】信号量代码
【2.1】代码1-打开信号量
Producer(item)
P(empty); // P是生产者,empty减1
...
V(full); // V是消费者,full 加1
// 用户态程序 producer.c
main()
sd = sem_open("empty"); // 打开信号量
for (i=1 to 5)
sem_wait(sd); // 写数据前,要看是否有空闲缓冲区;
write(fd, &i, 4); // 把5个数字(1 2 3 4 5) 写入到磁盘,每个数字4个字节
// sem.c // 进入内核
// 信号量结构体
typedef struct
char name[20]; // 信号量名称
int value; // 信号量值,一个整型变量
task_struct* queue; // 缓冲区队列
semtable[20];
// 系统调用:打开信号量或创建信号量
sys_sem_open(char *name)
在 semtable 中寻找name对上的项;
没找到则创建;
找到则返回对应下标;
// 写数据前,要看是否有空闲缓冲区
// 开关中断用于保护临界区
sys_sem_wait(int sd)
cli();// 关中断,不允许cpu响应其他中断请求,
// 不响应中断的目的是不响应时钟中断,从而不触发进程调度
// 这里判断信号量用的是if ,目的是唤醒第1个进程
if (semtable[sd].value-- < 0)
// 设置自己为阻塞;
// 将自己加入到 semtable[sd].queue 中;
schedule(); // 切换到其他进程执行
sti(); // 开中断
【2.2】代码2-读磁盘的信号量
// 读磁盘块
bread(int dev, int block)
struct buffer_head* bh; // 申请一段空闲缓冲区内存(用于映射磁盘块空间)
ll_rw_block(READ, bh); // 发起读命令
wait_on_buffer(bh); // 等待磁盘响应,然后读数据到缓冲区bh
// 启动磁盘读以后睡眠,
// 等待磁盘读完由磁盘中断将其唤醒,也是一种同步
// 开关中断用于保护临界区,临界区保证同时仅有一个进程操作信号量
lock_buffer(buffer_head* bh)
cli(); // 关中断
// 这里判断信号量用的是 while,目的是唤醒所有阻塞进程
while(bh->b_lock)
sleep_on(&bh->b_wait); // 如果被锁上,当前进程睡眠
bh->b_lock = 1; // b_lock也是信号量;这里上锁; 数据读完后,中断服务程序解锁;
sti(); // 开中断
// 当前进程阻塞(睡眠),切换到其他进程
void sleep_on(struct task_struct **p)
struct task_struct *tmp;
tmp = *p;
*p = current;
current->state = TASK_UNINTERRUPTIBLE; // 把进程状态更新为阻塞态
schedule(); // 调度, 底层调用switch_to()以切换到其他进程执行;
if (tmp)
tmp->state = 0;
【2.3】代码3-sleep_on形成的队列
【2.4】 代码4-从队列中唤醒阻塞进程
// 对阻塞进程的唤醒 (通过磁盘中断来唤醒)
static void read_intr(void)
...
end_request(1); // 磁盘准备数据完成后调用
end_request(int uptodate)
...
unlock_buffer(CURRENT->bh); // 解锁缓冲区
// 解锁缓冲区
unlock_buffer(struct buffer_head *bh)
bh->b_lock = 0;
wake_up(&bh->b_wait); // 唤醒阻塞的进程
// 唤醒阻塞的进程
wake_up(struct task_struct **p)
if (p && *p)
(**p).state = 0; // 把阻塞进程的state设置为0,该进程就变成就绪态了
*p = NULL;
注意:代码1用 if 判断信号量,代码2用 while 判断信号量,注意两者的区别;
以上是关于14.信号量的代码实现的主要内容,如果未能解决你的问题,请参考以下文章