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(&current_thread->locks)){
//没有锁则恢复捐赠前的优先级
current_thread->donated = false;
thread_set_priority(current_thread->old_priority);
}
else{
//还有其它的锁的情况
l = list_front(&current_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操作的更多相关文章

  1. [openmp]使用嵌套互斥锁锁定变量

    本文出自:http://www.cnblogs.com/svitter 转载请注明出处. 如果有一个线程必须要同时加锁两次,只能用嵌套型锁函数 函数名称 描述 void omp_init_nest_l ...

  2. day47 选择器优先级及嵌套关系

    复习 1.前端: 网页, html + css + js 2.html三个组成部分:标签,指令和转义字符 标签: <>包裹, 以字母开头, 可以结合-|数字, 能被浏览器解析的标记 3.常 ...

  3. Mongodb添加副本及修改优先级

    Mongodb添加副本及修改优先级 1.添加副本集 #在primary节点上执行 >rs.add( { host: "192.168.1.11:27017", priorit ...

  4. mysql:如何解决数据修改冲突(事务+行级锁的实际运用)

    摘要:最近做一个接诊需求遇到一个问题,假设一个订单咨询超过3次就不能再接诊,但如果两个医生同时对该订单进行咨询,查数据库的时候查到的接诊次数都是2次,那两个医生都能接诊,所谓接诊可以理解为更新了接诊次 ...

  5. linux c 线程间同步(通信)的几种方法--互斥锁,条件变量,信号量,读写锁

    Linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量.信号量和读写锁. 下面是思维导图:  一.互斥锁(mutex)  锁机制是同一时刻只允许一个线程执行一个关键部分的代码. 1 . ...

  6. Linux互斥锁、条件变量和信号量

    Linux互斥锁.条件变量和信号量  来自http://kongweile.iteye.com/blog/1155490 http://www.cnblogs.com/qingxia/archive/ ...

  7. 无法用排他锁锁定该数据库,以执行该操作。 (Microsoft SQL Server,错误: 5030)

    ALTER DATABASE Test_DB modify name = Howie --更改数据库名 EXEC sp_renamedb 'Howie' , 'Howie_Wee' --更改数据库名 ...

  8. ReentrantLock源码探究1:非公平锁的获取和释放

    1.AQS简单介绍 ​ Sync是ReentrantLock的一个内部类,它继承了AbstractQueuedSynchronizer,即AQS,在CountDownLatch.FutureTask. ...

  9. 如何证明sleep不释放锁,而wait释放锁?

    wait 加锁示例 public class WaitDemo { private static Object locker = new Object(); public static void ma ...

随机推荐

  1. angular 页面加载时可以调用 函数处理

    转载于 作者:海底苍鹰地址:http://blog.51yip.com/jsjquery/1599.html 我希望页面加载的时候,我能马上处理页面的数据,如请求API .... 所以这样设置 在某个 ...

  2. PAT1038. Recover the Smallest Number

    //意识到一个重要错误,一直以为atoi,itoa是windows独有的,linux下不可用,直到刚刚... //string+=比strcat好用多了,字符比较也方便的多,但是用scanf读入str ...

  3. ENVI 5.0 Beta 体验——影像数据的显示

    ENVI 5.0 Beta采用了全新的软件界面,数据的显示和操作跟以往的三视窗方式有很大的区别,下面一块体验一下. 对于栅格数据的显示方面,5.0有了非常大的改进,采用的全新的金字塔计算方法,在第一次 ...

  4. Jmeter笔记1:使用Badboy录制脚本,作为JMeter测试的素材

    接触Badboy,是因为JMeter要引用Badboy导出的脚本 Badboy的录制提供两个模式:Request(默认模式) 和navigation模式.点击下图N,切换模式:但是要导出到Jmeter ...

  5. _config.json

    { "AUTH": "66D86F40DF42A6103C2B0C2F16E41472DABF0594C79859E5EF51E06B377215F3B464E3F0F3 ...

  6. 动态设置屏幕icon

    REPORT demo_dynpro_status_icons.     DATA value TYPE i VALUE 1.     DATA: status_icon TYPE icons-tex ...

  7. Java设计模式(Design Patterns In Java)读书摘要——第1章 绪论

    为何需要模式 模式是做事的方法,是实现目标,研磨技术的方法.通俗点说,模式是为了解决某个行业的某个问题的有效的方法或技艺. 为何需要设计模式 为了提升代码的水准,是代码变得简洁而易用.模式是一种思想, ...

  8. 设备版本,设备号,APP版本,APP名称获取

    //获取设备id号 UIDevice *device = [UIDevice currentDevice];//创建设备对象 NSString *deviceUID = [[NSString allo ...

  9. 双人对战的球类游戏IOS源码

    双人对战的球类游戏源码,这个是一款双人对战的ios球类游戏源码,游戏的源码也比较详细的,我们在屏幕上下看到各有一个球门,内有一球,两边通过控制轮盘使球进入对方的球门的,其实玩法也很简单的,我们知道体育 ...

  10. 软件工程 speedsnail 冲刺2

    2015-5-6 完成任务:snail的对象: 遇到问题:关闭了speedsnail,背景音乐还在响: 明日任务:snail的移动功能.