Pintos修改优先级捐赠、嵌套捐赠、锁的获得与释放、信号量及PV操作
Pintos修改优先级捐赠、嵌套捐赠、锁的获得与释放、信号量及PV操作
原有的优先级更改的情况下面没有考虑到捐赠的情况,仅仅只是改变更改了当前线程的优先级,更别说恢复原本优先级了,所以不能通过任何有关捐赠的test。
原有的获得互斥锁和释放互斥锁的时候,仅仅是对信号量做一个简单的PV操作,获得互斥锁的时候应当考虑该锁当前是否被别的线程持有和优先级如何是否该被阻塞,释放互斥锁的时候也差不多同理,因此不能通过test。
原有的信号量操作仅仅是简单的加减,没有考虑信号量在不同值的情况下阻塞和唤醒的情况设计,因此不能通过test。
信号量的等待队列理应设计为优先队列。
释放锁时应该运行发生优先级抢占行为。
代码修改
给thread的定义增加了一些成员变量,并初始化
// 增加定义
int tick_blocked;
int old_priority;
struct list locks;
bool donated;
struct lock *blocked;
// 初始化
t->priority = t->old_priority = priority;
t->donated = false;
t->blocked = NULL;
list_init(&t->locks);
给lock增加成员变量,并初始化
/* Lock. */
struct lock
{
struct thread *holder; /* Thread holding lock (for debugging). */
struct semaphore semaphore; /* Binary semaphore controlling access. */
struct list_elem holder_elem;
int lock_priority;
};
void
lock_init (struct lock *lock)
{
ASSERT (lock != NULL);
lock->holder = NULL;
sema_init (&lock->semaphore, 1);
lock->lock_priority = -1;
}
一开始的时候随便给lock_priority设置一个最低的优先级即可,只要不影响代码正确运行即可
重新设计优先级更改的情况
单独捐赠的情况:
当高优先级线程因为低优先级线程占用资源而阻塞时,应将低优先级线程的优先级提升到等待它所占有的资源的最高优先级线程的优先级。即将优先级较高的线程的优先级 donate 给持有资源的优先级的进程,让其先执行。

多重捐赠的情况:
struct thread 中的 list locks, 还有 struct locks 中的 lock_priority,两者配合使用,应对 multiple-donate 的情况。由于每次 donate 的时候都是因为优先级高的一个进程需要申请一个握在优先级比较低的线程手中的,因此锁在涉及到 priority-donate 的时候维护一个lock_priority,记录获得这个锁的线程此时的优先级,因为存在 multiple-donate,线程可能会接受几个不同的优先级,因此需要在锁中,而不是在线程的结构中维护这样一个信息,以在释放锁,undonate 的时候能够将线程优先级恢复到正确的值。

嵌套捐赠的情况:
通过检测被捐赠的线程是否已经获得了所需要的全部锁来判断是否出现嵌套捐赠的情况,如是则设置好参数来进行下一轮的优先级捐赠。和情况一差不多,也就是多捐赠一次,知道被捐赠线程获得了所需要的全部的锁。

void
thread_set_priority(int new_priority)
{
//重定向thread_set_priority函数
thread_set_priority_fixed(thread_current(), new_priority, true);
}
重定向原本的优先级更改函数,一个参数已经不能满足这次的实验需求了
void
thread_set_priority_fixed(struct thread *current_thread, int new_priority, bool nest){
enum intr_level old_level;
old_level = intr_disable();
//没被捐赠过的同时改变两个变量
if(current_thread->donated == false){
current_thread->priority = current_thread->old_priority = new_priority;
}
//正在被捐赠
else if(nest){
//捐赠过的,且新优先级比当前优先级还小,更新旧优先级
if(current_thread->priority > new_priority){
current_thread->old_priority = new_priority;
}
//捐赠过的,更新当前优先级
else{
current_thread->priority = new_priority;
}
}
//捐赠过的但取消捐赠状态,更新当前优先级
else{
current_thread->priority = new_priority;
}
//就绪队列中优先级比新设置的优先级低,则让出CPU
if(list_entry(list_begin(&ready_list), struct thread, elem)->priority > new_priority){
thread_yield();
}
intr_set_level(old_level);
}
其实一共就4种情况:
没被捐赠过的,直接更新priority和old_priority两个变量
正在被捐赠需要改优先级的且新优先级被当前优先级更低,更新old_priority
正在被捐赠需要改优先级的且新优先级被当前优先级高,更新priority
取消捐赠状态,恢复成旧优先级,更新priority
最后还有加一个优先级抢占的行为。
获得互斥锁的更改
和上述的表达一致,主要在于获得互斥锁的时候判断是否需要产生捐赠,能否获得互斥锁,以及捐赠结束后更新锁的链表
void
lock_acquire (struct lock *lock)
{
ASSERT (lock != NULL);
ASSERT (!intr_context ());
ASSERT (!lock_held_by_current_thread (lock));
enum intr_level old_level;
old_level = intr_disable();
struct thread* current_thread = thread_current();
struct thread* lockholder_thread = lock->holder;
struct lock* another;
//假设当前线程会被该锁阻塞,如不会后面有解锁操作
current_thread->blocked = another = lock;
//该锁当前被别的线程持有且优先级比当前线程低
while(lockholder_thread != NULL && lockholder_thread->priority < current_thread->priority){
//持有锁的线程需要被捐赠以提高优先级
lockholder_thread->donated = true;
//把当前线程的优先级捐赠给锁的持有者
thread_set_priority_fixed(lockholder_thread, current_thread->priority, true);
//捐赠结束后修改锁的优先级
if(another->lock_priority < current_thread->priority){
another->lock_priority = current_thread->priority;
list_remove(&another->holder_elem);
list_insert_ordered(&lockholder_thread->locks, &another->holder_elem, cmp_priority2, NULL);
}
//假如被捐赠的线程还缺少锁,则更新下一轮的参数,嵌套捐赠
if(lockholder_thread->status == THREAD_BLOCKED && lockholder_thread->blocked != NULL){
another = lockholder_thread->blocked;
lockholder_thread = lockholder_thread->blocked->holder;
}
//否则跳出循环,捐赠结束
else{
break;
}
}
//进行P操作,直到拿到锁
sema_down (&lock->semaphore);
lock->holder = current_thread;
lock->lock_priority = current_thread->priority;
current_thread->blocked = NULL;
//更新当前线程获得的锁的链表
list_insert_ordered(&lock->holder->locks, &lock->holder_elem, cmp_priority2, NULL);
intr_set_level(old_level);
}
注意一点的是,锁的记录链表是用优先队列来实现的,而不是使用普通队列。
释放互斥锁的操作
释放锁后主要有三种情况:
该线程已经没有锁了:恢复捐赠前的优先级
还有其它的锁:恢复成其它锁的最高优先级
因为最后的锁而没有锁了,恢复捐赠前的优先级
void
lock_release (struct lock *lock)
{
struct thread * current_thread = thread_current();
ASSERT(current_thread->blocked == NULL);
ASSERT (lock != NULL);
ASSERT (lock_held_by_current_thread (lock));
enum intr_level old_level;
old_level = intr_disable();
struct list_elem *l;
struct lock *another;
//释放锁的操作
lock->holder = NULL;
list_remove(&lock->holder_elem);
lock->lock_priority = PRI_MIN - 1;
sema_up (&lock->semaphore);
//处理该线程拥有的其余的锁
if(list_empty(¤t_thread->locks)){
//没有锁则恢复捐赠前的优先级
current_thread->donated = false;
thread_set_priority(current_thread->old_priority);
}
else{
//还有其它的锁的情况
l = list_front(¤t_thread->locks);
another = list_entry(l, struct lock, holder_elem);
if(another->lock_priority != PRI_MIN - 1){
//该情况锁的优先级即为当前线程得到锁时的优先级
thread_set_priority_fixed(current_thread, another->lock_priority, false);
}
else{
//该情况恢复原本的优先级
thread_set_priority(current_thread->old_priority);
}
}
intr_set_level(old_level);
}
这里恢复线程的优先级的时候,是通过set_priority函数发生抢占行为的,和获得锁的操作有所不一样。
信号量的P操作
这里主要增加一个能否得到信号量,不能则应该被阻塞,加这个操作就行了
void
sema_down (struct semaphore *sema)
{
enum intr_level old_level;
ASSERT (sema != NULL);
ASSERT (!intr_context ());
old_level = intr_disable ();
//线程一直等待其他线程产生V操作
while (sema->value == 0)
{
//当前线程一定不在就绪队列中,不会重复插入
list_insert_ordered(&sema->waiters, &thread_current()->elem, cmp_priority, NULL);
thread_block ();
}
sema->value--;
intr_set_level (old_level);
}
信号量的V操作
V操作需要选择一个等待线程来唤醒,如果唤醒的线程比当前线程优先级更高的话,则允许发生抢占行为
void
sema_up (struct semaphore *sema)
{
enum intr_level old_level;
struct thread* current_thread;
struct thread* wake_up;
ASSERT (sema != NULL);
old_level = intr_disable ();
wake_up = NULL;//即将被唤醒的线程
current_thread = thread_current();
//从信号量的waiter队列中选一个出来唤醒
if (!list_empty (&sema->waiters)){
//避免多重锁的时候队列变成无序,先排序
list_sort(&sema->waiters, cmp_priority, NULL);
wake_up = list_entry(list_pop_front(&sema->waiters), struct thread, elem);
thread_unblock(wake_up);
//thread_unblock (list_entry (list_pop_front (&sema->waiters), struct thread, elem));
}
//信号量V操作
sema->value++;
//多重锁的时候,捐赠后的被唤醒线程优先级比当前线程高时,当前线程让出CPU
if(wake_up != NULL && wake_up->priority > current_thread->priority){
thread_yield();
}
intr_set_level (old_level);
}
这里要多个锁和多个信号量的情况,会造成等待队列无序(其实应该也不会,哪怕会,加上也不会错,反正已经有序了排序也是一瞬间的事情=。=)。
至此,该部分完成了。
传送门
开源学习地址:
https://github.com/Wsine/pintos-ubuntu
这部分的Commit:
https://github.com/Wsine/pintos-ubuntu/tree/a472c6255de5874a618b337d729736c56142ee1a
Pintos修改优先级捐赠、嵌套捐赠、锁的获得与释放、信号量及PV操作的更多相关文章
- [openmp]使用嵌套互斥锁锁定变量
本文出自:http://www.cnblogs.com/svitter 转载请注明出处. 如果有一个线程必须要同时加锁两次,只能用嵌套型锁函数 函数名称 描述 void omp_init_nest_l ...
- day47 选择器优先级及嵌套关系
复习 1.前端: 网页, html + css + js 2.html三个组成部分:标签,指令和转义字符 标签: <>包裹, 以字母开头, 可以结合-|数字, 能被浏览器解析的标记 3.常 ...
- Mongodb添加副本及修改优先级
Mongodb添加副本及修改优先级 1.添加副本集 #在primary节点上执行 >rs.add( { host: "192.168.1.11:27017", priorit ...
- mysql:如何解决数据修改冲突(事务+行级锁的实际运用)
摘要:最近做一个接诊需求遇到一个问题,假设一个订单咨询超过3次就不能再接诊,但如果两个医生同时对该订单进行咨询,查数据库的时候查到的接诊次数都是2次,那两个医生都能接诊,所谓接诊可以理解为更新了接诊次 ...
- linux c 线程间同步(通信)的几种方法--互斥锁,条件变量,信号量,读写锁
Linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量.信号量和读写锁. 下面是思维导图: 一.互斥锁(mutex) 锁机制是同一时刻只允许一个线程执行一个关键部分的代码. 1 . ...
- Linux互斥锁、条件变量和信号量
Linux互斥锁.条件变量和信号量 来自http://kongweile.iteye.com/blog/1155490 http://www.cnblogs.com/qingxia/archive/ ...
- 无法用排他锁锁定该数据库,以执行该操作。 (Microsoft SQL Server,错误: 5030)
ALTER DATABASE Test_DB modify name = Howie --更改数据库名 EXEC sp_renamedb 'Howie' , 'Howie_Wee' --更改数据库名 ...
- ReentrantLock源码探究1:非公平锁的获取和释放
1.AQS简单介绍 Sync是ReentrantLock的一个内部类,它继承了AbstractQueuedSynchronizer,即AQS,在CountDownLatch.FutureTask. ...
- 如何证明sleep不释放锁,而wait释放锁?
wait 加锁示例 public class WaitDemo { private static Object locker = new Object(); public static void ma ...
随机推荐
- SQL Server 之登录
1:SQL Server 是Microsoft 公司推出的关系型数据库管理系统. 安装好后,开始登录. 2:几种登录方式: (1):服务器名称: ①: . :代表本地服务器 ②: local :代表本 ...
- Android IOS WebRTC 音视频开发总结(五七)-- 网络传输上的一种QoS方案
本文主要介绍一种QoS的解决方案,文章来自博客园RTC.Blacker,欢迎关注微信公众号blacker,更多详见www.rtc.help QoS出现的背景: 而当网络发生拥塞的时候,所有的数据流都有 ...
- CICS的database中R D中参数的含义
见链接 http://blog.163.com/ajj_star/blog/static/1626772542010328113513429/ Region Definitions (RD) 定义了所 ...
- SAP物料价格评估与成本计算体系
物料评估目的 核算物料领用与消耗的价值,使生产成本与产品销售成本有统一计价标准. 核算企业库存价值. 物料价格方法 移动平均价,标准价,实际价,计划价. @移动平均价: 每次物料移动后重新计算平均价, ...
- 查看静态库(.a文件)内容
可执行代码的二进制格式,看不到. 1.打开Terminal(终端), cd 到 .a包所在的文件夹 2.输入命令:lipo -info libtrafficstatisticszyf.a 结果如下:
- Vue.js学习 Item14 – 过滤器与自定义过滤器
基础 类似于自定义指令,可以用全局方法 Vue.filter() 注册一个自定义过滤器,它接收两个参数:过滤器 ID 和过滤器函数.过滤器函数以值为参数,返回转换后的值: Vue.filter('re ...
- Miniprofiler在普通net项目中的使用
1.Global.asax中配置 Void Application_BeginRequest(Object sender, EventArgs e){ If(Request.IsLocal){ //请 ...
- XP极限编程
13个核心实战 团队协作(Whole Team) 规划策略(The Planning Game) 软件发布计划(ReleasePlanning) 周期开发计划(IterationPlanning) ...
- 配置NTP服务ntpd/ntp.conf(搭建Hadoop集群可参考)
本文拟定是在一个局域网内(比如一个Hadoop集群)设定一台NTP服务器作为整个网络的标准时间参考,使用网络(集群)内的所有机器保持时间一致!以下是详细的操作步骤: 1. 修改选定的服务器的本地时间 ...
- mysql批量修改表引擎
生成修改的语句 SELECT CONCAT('ALTER TABLE ',table_name,' ENGINE=InnoDB;') FROM information_schema.tables WH ...