zoukankan      html  css  js  c++  java
  • ucore学习笔记_LAB7

    练习1: 理解内核级信号量的实现和基于内核级信号量的哲学家就餐问题(不需要编码)

    信号量结构体:

    typedef struct {
        int value;//资源数目
        wait_queue_t wait_queue;//等待队列
    } semaphore_t;

    P,V操作:

    static __noinline void __up(semaphore_t *sem, uint32_t wait_state) {
        bool intr_flag;
        local_intr_save(intr_flag);
        {
            wait_t *wait;
            //如果没有正在等待的进程,直接资源++
            if ((wait = wait_queue_first(&(sem->wait_queue))) == NULL) {
                sem->value ++;
            }
            //否则唤醒位于等待队列中的进程
            else {
                assert(wait->proc->wait_state == wait_state);
                wakeup_wait(&(sem->wait_queue), wait, wait_state, 1);
            }
        }
        local_intr_restore(intr_flag);
    }
    
    static __noinline uint32_t __down(semaphore_t *sem, uint32_t wait_state) {
        bool intr_flag;
        local_intr_save(intr_flag);//关中断
        //如果资源数大于零直接资源--,返回
        if (sem->value > 0) {
            sem->value --;
            local_intr_restore(intr_flag);
            return 0;
        }
        wait_t __wait, *wait = &__wait;
        wait_current_set(&(sem->wait_queue), wait, wait_state);//挂起
        local_intr_restore(intr_flag);//开中断
    
        schedule();//等待被唤醒
    
        local_intr_save(intr_flag);
        //重新调度
        wait_current_del(&(sem->wait_queue), wait);
        local_intr_restore(intr_flag);
    
        if (wait->wakeup_flags != wait_state) {
            return wait->wakeup_flags;
        }
        return 0;
    }

    底层支持:

    定时器、屏蔽/使能中断、等待队列wait_queue支持test_and_set_bit等原子操作机器指令

    在ucore信号量中使用的是开关中断.

    练习2: 完成内核级条件变量和基于内核级条件变量的哲学家就餐问题(需要编码)

    管程由四部分组成:

    ​ 管程内部的共享变量;
    ​ 管程内部的条件变量;
    ​ 管程内部并发执行的进程;
    ​ 对局部于管程内部的共享数据设置初始值的语句。

    条件变量(CV)和管程结构体:

    typedef struct condvar{
        semaphore_t sem;        // the sem semaphore  is used to down the waiting proc, and the signaling proc should up the waiting proc
        int count;              // the number of waiters on condvar
        monitor_t * owner;      // the owner(monitor) of this condvar
    } condvar_t;
    
    typedef struct monitor{
        semaphore_t mutex;      // the mutex lock for going into the routines in monitor, should be initialized to 1
        semaphore_t next;       // the next semaphore is used to down the signaling proc itself, and the other OR wakeuped waiting proc should wake up the sleeped signaling proc.
        int next_count;         // the number of of sleeped signaling proc
        condvar_t *cv;          // the condvars in monitor
    } monitor_t;

    哎这个next一开始搞得我很迷,其实就是说由于唤醒别人而自己睡着的进程个数。他们被唤醒是无条件的,只要保证对管程的互斥访问即可。

    每一个条件变量有它自己的等待队列,那么这个next是由于唤醒了别人,自己被挂在next上了,每个进程执行结束之后先查一查有没有挂在next上的进程,有的话先唤醒这个进程(Vnext),没有的话就放其他进程进来(Vmutex)。

    条件等待和唤醒:

    void 
    cond_signal (condvar_t *cvp) {
       //LAB7 EXERCISE1: YOUR CODE
       cprintf("cond_signal begin: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);  
      /*
       *      cond_signal(cv) {
       *          if(cv.count>0) {
       *             mt.next_count ++;
       *             signal(cv.sem);
       *             wait(mt.next);
       *             mt.next_count--;
       *          }
       *       }
       */
         if(cvp->count>0) {
            cvp->owner->next_count ++;
            up(&(cvp->sem));
            down(&(cvp->owner->next));
            cvp->owner->next_count --;
          }
       cprintf("cond_signal end: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
    }
    
    // Suspend calling thread on a condition variable waiting for condition Atomically unlocks 
    // mutex and suspends calling thread on conditional variable after waking up locks mutex. Notice: mp is mutex semaphore for monitor's procedures
    void
    cond_wait (condvar_t *cvp) {
        //LAB7 EXERCISE1: YOUR CODE
        cprintf("cond_wait begin:  cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
       /*
        *         cv.count ++;
        *         if(mt.next_count>0)
        *            signal(mt.next)
        *         else
        *            signal(mt.mutex);
        *         wait(cv.sem);
        *         cv.count --;
        */
          cvp->count++;
          if(cvp->owner->next_count > 0)
             up(&(cvp->owner->next));
          else
             up(&(cvp->owner->mutex));
          down(&(cvp->sem));
          cvp->count --;
        cprintf("cond_wait end:  cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
    }

    管程中函数的入口出口设计

    为了让整个管程正常运行,还需在管程中的每个函数的入口和出口增加相关操作,即:

    function_in_monitor (…)
    {
      sem.wait(monitor.mutex);
    //-----------------------------
      the real body of function;
    //-----------------------------
      if(monitor.next_count > 0)
         sem_signal(monitor.next);
      else
         sem_signal(monitor.mutex);
    }

    这样带来的作用有两个,(1)只有一个进程在执行管程中的函数。(2)避免由于执行了cond_signal函数而睡眠的进程无法被唤醒。对于第二点,如果进程A由于执行了cond_signal函数而睡眠(这会让monitor.next_count大于0,且执行sem_wait(monitor.next)),则其他进程在执行管程中的函数的出口,会判断monitor.next_count是否大于0,如果大于0,则执行sem_signal(monitor.next),从而执行了cond_signal函数而睡眠的进程被唤醒。上诉措施将使得管程正常执行。

    吐槽

    不得不说,MOOC上这里讲的不太清楚,那个next绕来绕去,其实是很容易能解释清楚的,我们这里实现的管程属于Hoare语义,即

    • 线程B执行signal之后,迅速唤醒等待中的线程A,自己进入signal队列中(这个队列是此语义特有的)
    • 每次有线程退出时,先到signal队列中调度线程,如果signal队列空,才到入口等待队列调度线程
    • 实际实现中一般不采用,因为需要多一个队列,代价增大了
  • 相关阅读:
    Windows环境下OpenLDAP安装配置
    jobcenter在Windows下连携LDAP
    OpenLDAP搭建
    Go 函数 #3
    Go 数组/多维数组/切片/map #2
    Go内置类型/变量/常量 #1
    git常用命令
    makefile基础_1
    kubernete的service
    配置开发环境
  • 原文地址:https://www.cnblogs.com/obob/p/11767012.html
Copyright © 2011-2022 走看看