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 判断信号量,注意两者的区别

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值