18.进程同步与死锁——信号量的代码实现
来源:互联网 发布:网络即时通讯工具问卷 编辑:程序博客网 时间:2024/05/23 20:00
1.生产者代码
//伪代码Producer(item){ P(empty); ... V(full);}//sem.c 进入内核//信号量在内核中,包含 value 和 PCB,value的值能被多个进程看到typedef struct{ char name[20];// name是信号量的名字,比如empty int value; // value记录供进程判断的值 task_struct * queue; // PCB队列} semtable[20];// sem_open的最终实现sys_sem_open(char *name){ 在semtable中根据name寻找对应的元素; 如果没找到就创建这个元素; 返回对应的下标;}
//代码实现 Producer.cmain(){ sd = sem_open("empty");//通过系统调用sem_open打开内核,共同使用empty这个信号量 //执行5次,在文件中写出5个数字,每个数字占4个字节 for(i = 1 to 5){ sem_wait(sd); // 判断是否有空闲缓冲区 write(fd, &i, 4); }}// sd是下标sys_sem_wait(int sd){ cli(); // 根据下标取value,value-- < 0 说明没有空闲缓冲区了 if(semtable[sd].value-- < 0){ 设置自己为阻塞; 将自己加入semtable[sd].queue中; schedule(); } sti();}// value是共享的,操作value要设置 开关中断,单CPU可以用cli() sti()V(full){ cli(); if(semtable[sd].value++ >= 0) { 从semtable[sd].queue中取出一个进程,设置为就绪态; } sti();}
2.Linux 0.11 的实现
2.1 从磁盘读数据
// fs/buffer.c/** bread() reads a specified block and returns the buffer that contains* it. It returns NULL if the block was unreadable.*//** 从设备上读取指定的数据块并返回含有数据的缓冲区。如果指定的块不存在* 则返回NULL。*/// 从指定设备上读取指定的数据块//用户程序发出read指令,就要进入内核,执行sys_read,最终执行的是breadstruct buffer_head *bread (int dev, int block){ struct buffer_head *bh;// 申请一块空闲缓冲区 ...// 启动读的命令,就要阻塞了 ll_rw_block (READ, bh); wait_on_buffer (bh);//bh是信号量 ...}
// kernel/blk_drv/ll_rw_blk.c// 锁定指定的缓冲区bh。如果指定的缓冲区已经被其它任务锁定,则使自己睡眠(不可中断地等待),// 直到被执行解锁缓冲区的任务明确地唤醒。static inline voidlock_buffer (struct buffer_head *bh){ cli ();// 开关保护 while (bh->b_lock) // 如果缓冲区已被锁定,则睡眠,直到缓冲区解锁。 sleep_on (&bh->b_wait); bh->b_lock = 1;// 立刻锁定该缓冲区。b_lock是信号量,1表示上锁,没读完。读完了,中断会解锁,其他进程要读,会判断 b_lock,如果是1,锁上了,进程会睡眠 sti ();// 开中断。}
我们之前使用的信号量是 if 判断的,上边用的是 while 判断的
// kernel\sched.c// 把当前任务置为不可中断的等待状态,并让睡眠队列头的指针指向当前任务。// 只有明确地唤醒时才会返回。该函数提供了进程与中断处理程序之间的同步机制。// 函数参数*p 是放置等待任务的队列头指针。p是指向 task_struct结构体的指针的指针void sleep_on (struct task_struct **p){ struct task_struct *tmp; // tmp是一个局部变量 ... //下边2句是最隐蔽的队列,1.将自己放到阻塞队列中 tmp = *p; // tmp 指向已经在等待队列上的任务(如果有的话),tmp指向 task_struct,之前的队首,tmp保存在内核栈中,根据tmp可以找到下一个进程的内核栈 *p = current; // 将睡眠队列头的等待指针指向当前任务。新的阻塞队列的队首是current current->state = TASK_UNINTERRUPTIBLE; // 2.将当前任务置为不可中断的等待状态,阻塞态 schedule (); // 调度,切换到别的进程去执行 // 只有当这个等待任务被唤醒时,调度程序才又返回到这里,则表示进程已被明确地唤醒。 // 既然大家都在等待同样的资源,那么在资源可用时,就有必要唤醒所有等待该资源的进程。 // 该函数嵌套调用,也会嵌套唤醒所有等待该资源的进程。然后系统会根据这些进程的优先条件, // 重新调度应该由哪个进程首先使用资源。也即让这些进程竞争上岗。 // tmp 是被唤醒的进程的变量,是被唤醒的进程的下一个进程 if (tmp) // 若还存在等待的任务,则也将其置为就绪状态(唤醒)。 tmp->state = 0; // 如果有下一个进程,就把下一个进程唤醒}
2.2 如何从Linux 0.11 的这个队列唤醒?
// kernel\blk_drv\hd.c//// 读操作中断调用函数。将在执行硬盘中断处理程序中被调用。static void read_intr (void){ // 磁盘中断 ... end_request (1); // 若全部扇区数据已经读完,则处理请求结束事宜, do_hd_request (); // 执行其它硬盘请求操作。}// 结束请求。extern inline voidend_request (int uptodate){ ... unlock_buffer (CURRENT->bh); // 解锁缓冲区。 ...}// 释放锁定的缓冲区。extern inline voidunlock_buffer (struct buffer_head *bh){ if (!bh->b_lock) // 如果指定的缓冲区bh 并没有被上锁,则显示警告信息。 printk (DEVICE_NAME ": free buffer being unlocked\n"); bh->b_lock = 0; // 否则将该缓冲区解锁。 wake_up (&bh->b_wait); // 唤醒等待该缓冲区的进程。}
// kernel\sched.c// 唤醒指定任务*p。voidwake_up (struct task_struct **p){ if (p && *p) { (**p).state = 0; // 置为就绪(可运行)状态,唤醒队首 *p = NULL; }}
一个进程被唤醒,从上一次 切出去的地方:sleep_on 调用完 schedule后 继续执行
为什么用while?
wake_up 唤醒队首进程,队首执行,再唤醒下一个进程
下一个进程也从schedule开始执行,执行时 再把当前进程的 下一个进程唤醒
while 是逐渐将阻塞队列的进程 全部唤醒,if只能唤醒阻塞队列的第一个进程
假设 进程1要等待一个事件,阻塞了,进程2也等待这个事件,也阻塞了
用 if 唤醒,进程1 总是优先执行
while 是将所有的进程都唤醒,再由schedule决定执行哪个进程,优先级高的进程执行,而不是按照队列顺序,lock_buffer 中会 再执行 while(bh->b_lock), 如果没锁,bh->b_block = 1; 自己执行,上锁。
之前阻塞的队列中的进程 已经全部为就绪态,执行时,会判断 while(bh->b_lock),如果锁了,就会睡眠
while 不需要记录有多少进程在阻塞,每次都是全部唤醒,下次再判断,信号量不用累加
0 0
- 18.进程同步与死锁——信号量的代码实现
- 16.进程同步与死锁——进程同步与信号量
- 17.进程同步与死锁——信号量临界区保护
- 19.进程同步与死锁——死锁处理
- 信号量实现进程同步
- 进程同步与信号量
- 信号量实现多进程的同步访问
- 睡觉理发师问题——进程同步与死锁
- OS学习笔记——进程同步与死锁1
- 操作系统--进程同步与死锁
- linux 命名信号量实现进程间的互斥与同步
- linux 命名信号量实现进程间的互斥与同步
- 操作系统之进程与线程5——进程同步与信号量
- 用system V信号量实现进程同步的例子
- Posex信号量 实现进程间的同步(生产者&消费者)
- 用信号量解决进程的同步与互斥探讨
- 用信号量解决进程的同步与互斥探讨
- 用信号量解决进程的同步与互斥
- Linux 搭建SVN 服务器 CENTOS
- Ubuntu环境下使用OpenOCD调试目标平台
- 蓝桥杯——2011 HIT计算机研究生机试真题(2017.1.31)
- mysql基础
- 每天读一点好玩心理学--电梯
- 18.进程同步与死锁——信号量的代码实现
- BZOJ 1090: [SCOI2003]字符串折叠 区间动归
- nodejs开发——http模块
- 关联规则挖掘-频繁模式挖掘
- 每天读一点好玩心理学--酒吧
- arrayList hashset的比较及hashcode分析
- ext可视化工具Sencha Architect 3破解
- Linux C 字符串操作
- 莫队算法及其应用