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)信号量定义:
2)操作系统内部也是有信号量来实现多进程同步合作
3)借助信号量可以完成的工作
4)信号量数据结构
-
- 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(); // 开中断
- }
-
- // 读磁盘块
- 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;
- }
-
- // 对阻塞进程的唤醒 (通过磁盘中断来唤醒)
- 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 判断信号量,注意两者的区别;