对kern/trap/trap.c中的lab6 code中的sched_class_proc_tick(current)改成run_timer_list()
case IRQ_OFFSET + IRQ_TIMER:
ticks ++;
assert(current != NULL);
run_timer_list(); //这里
break;
可以看出内核信号量结构体中就是个值,和队列而已
typedef struct {
int value;
wait_queue_t wait_queue;
} semaphore_t;
void sem_init(semaphore_t *sem, int value);
void up(semaphore_t *sem);
void down(semaphore_t *sem);
bool try_down(semaphore_t *sem);
首先分析 up函数(完成V操作),发现只是调用了_up把sem传入了
void
up(semaphore_t *sem) {
__up(sem, WT_KSEM);//WT_KSEM: 0x00000100 //等待内核信号量
}
然后来看一下_up
大致手段就是禁用中断的方式来处理(在单核有效,多核无效)
禁用中断后,首先判断是否有等待的,没有就把值++
有的话直接唤醒他就行(就是简化了一下++之后在–,所以就直接唤醒就行,方便写)
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)
}
然后看一下down操作(P操作)类似调用了_down
void
down(semaphore_t *sem) {
uint32_t flags = __down(sem, WT_KSEM);
assert(flags == 0);
}
首先关中断
然后查看value,如果有的话就–然后恢复中断了
没有value的话,把当前放到等待队列里,然后执行调度(让别人去占用cpu)
然后等别人有value了把自己叫醒
关中断,把等待链表上的自己删掉,恢复中断
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;
}
value>0,表示共享资源的空闲数
vlaue<0,表示该信号量的等待队列里的进程数
value=0,表示等待队列为空
//---------- philosophers problem using semaphore ----------------------
int state_sema[N]; /* 记录每个人状态的数组 */
/* 信号量是一个特殊的整型变量 */
semaphore_t mutex; /* 临界区互斥 */
semaphore_t s[N]; /* 每个哲学家一个信号量 */
说白了就是 先弄个互斥锁,然后看看自己能抢不,不能让别人到时候调用我的phi_test_sema去抢
void phi_take_forks_sema(int i) /* i:哲学家号码从0到N-1 */
{
down(&mutex); /* 进入临界区 */
state_sema[i] = HUNGRY; /* 记录下哲学家i饥饿的事实 */
phi_test_sema(i); /* 试图得到两只叉子 */
up(&mutex); /* 离开临界区 */
down(&s[i]); /* 如果得不到叉子就阻塞 */
}
这个就是 吃饭的人不吃了,然后看看左边的人想吃不,和右边的人想吃不
void phi_put_forks_sema(int i) /* i:哲学家号码从0到N-1 */
{
down(&mutex); /* 进入临界区 */
state_sema[i] = THINKING; /* 哲学家进餐结束 */
phi_test_sema(LEFT); /* 看一下左邻居现在是否能进餐 */
phi_test_sema(RIGHT); /* 看一下右邻居现在是否能进餐 */
up(&mutex); /* 离开临界区 */
}
整体就是,一个信号量表示公共的互斥操作,五个信号量(也算是条件变量了)表示状态(阻塞,非阻塞)
自己阻塞的时候,通过别人调用下面的函数来唤醒
void phi_test_sema(i) /* i:哲学家号码从0到N-1 */
{
if (state_sema[i] == HUNGRY && state_sema[LEFT] != EATING && state_sema[RIGHT] != EATING)
{
state_sema[i] = EATING;
up(&s[i]);
}
}
每个线程的操作函数
int philosopher_using_semaphore(void *arg) /* i:哲学家号码,从0到N-1 */
{
int i, iter = 0;
i = (int)arg;
cprintf("I am No.%d philosopher_sema\n", i);
while (iter++ < TIMES)
{ /* 无限循环 */
cprintf("Iter %d, No.%d philosopher_sema is thinking\n", iter, i); /* 哲学家正在思考 */
do_sleep(SLEEP_TIME);
phi_take_forks_sema(i);
/* 需要两只叉子,或者阻塞 */
cprintf("Iter %d, No.%d philosopher_sema is eating\n", iter, i); /* 进餐 */
do_sleep(SLEEP_TIME);
phi_put_forks_sema(i);
/* 把两把叉子同时放回桌子 */
}
cprintf("No.%d philosopher_sema quit\n", i);
return 0;
}
内核级条件变量的哲学家就餐问题在check_sync
处实现。同信号量的测试相似,这里也是创建了5个内核进程表示5个哲学家的行为。
void check_sync(void){
int i;
//check condition variable
monitor_init(&mt, N);
for(i=0;i<N;i++){
state_condvar[i]=THINKING;
int pid = kernel_thread(philosopher_using_condvar, (void *)i, 0);
if (pid <= 0) {
panic("create No.%d philosopher_using_condvar failed.\n");
}
philosopher_proc_condvar[i] = find_proc(pid);
set_proc_name(philosopher_proc_condvar[i], "philosopher_condvar_proc");
}
每个线程也是调用phi_test_condvar(i)尝试自己获取到叉子,自己获取不到,那就让别人帮自己调用phi_test_condvar这个函数来获取
同时在尝试获取不到叉子时候调用 cond_wait(&mtp->cv[i]);阻塞自己,等待别人唤醒,因为有可能唤醒之后,自己还是没有EATING所以要用循环判断,直到自己获取到了
void phi_take_forks_condvar(int i)
{
down(&(mtp->mutex));
//--------into routine in monitor--------------
// LAB7 EXERCISE1: YOUR CODE
// I am hungry
state_condvar[i] = HUNGRY; /* 记录下哲学家i饥饿的事实 */
// try to get fork
phi_test_condvar(i);
while (state_condvar[i] != EATING)
{
cprintf("phi_take_forks_condvar: %d didn't get fork and will wait\n", i);
cond_wait(&mtp->cv[i]);
}
//--------leave routine in monitor--------------
if (mtp->next_count > 0)
up(&(mtp->next));
else
up(&(mtp->mutex));
}
其中下面是管程的操作
down(&(mtp->mutex));
。。。。
//--------leave routine in monitor--------------
if (mtp->next_count > 0)
up(&(mtp->next));
else
up(&(mtp->mutex));
}
一个管程定义了一个数据结构和能为并发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据。管程由以下四部分组成:
①管程内部的共享变量;
②管程内部的条件变量;
③管程内部并发执行的进程;
④对局部于管程内部的共享数据设置初始值的语句。
即一个管程由一个锁和多个条件变量组成。由此可见,管程相当于一个隔离区,它把共享变量和对它进行操作的若干个过程围了起来,所有进程要访问临界资源时,都必须经过管程才能进入,而管程每次只允许一个进程进入管程,从而需要确保进程之间互斥。
typedef struct monitor monitor_t;
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;
管程中的成员变量mutex是一个二值信号量,是实现每次只允许一个进程进入管程的关键元素,确保了互斥访问性质。管程中的条件变量cv通过执行wait_cv,会使得等待某个条件C为真的进程能够离开管程并睡眠,且让其他进程进入管程继续执行;而进入管程的某进程设置条件C为真并执行signal_cv时,能够让等待某个条件C为真的睡眠进程被唤醒,从而继续进入管程中执行。管程中的成员变量信号量next和整形变量next_count是配合进程对条件变量cv的操作而设置的,这是由于发出signal_cv的进程A会唤醒睡眠进程B,进程B执行会导致进程A睡眠,直到进程B离开管程,进程A才能继续执行,这个同步过程是通过信号量next完成的;而next_count表示了由于发出singal_cv而睡眠的进程个数。
然后下面就是实现管程中条件变量(信号量模拟)的函数
void cond_signal(condvar_t *cvp)
{
if (cvp->count > 0) //确保有人在阻塞
{
cvp->owner->next_count++;//管程只能同时有一个线程在运行,所以要把自己挂起来
up(&cvp->sem);//唤醒别人
down(&cvp->owner->next);//阻塞自己
cvp->owner->next_count--;//自己阻塞没了就--
}
}
void cond_wait(condvar_t *cvp)
{
cvp->count++;//等待数++
if(cvp->owner->next_count > 0){ // 如果说别人是因为条件变量阻塞的
up(&cvp->owner->next);
}else{//是不是条件变量阻塞的就是互斥锁阻塞
up(&cvp->owner->mutex);
}
down(&cvp->sem);//阻塞自己
cvp->count--;//接触阻塞
}
下面是哲学家操作
void phi_take_forks_condvar(int i)
{
down(&(mtp->mutex));
//--------into routine in monitor--------------
// LAB7 EXERCISE1: YOUR CODE
// I am hungry
state_condvar[i] = HUNGRY; /* 记录下哲学家i饥饿的事实 */
phi_test_condvar(i); // try to get fork
while (state_condvar[i] != EATING)
{
cprintf("phi_take_forks_condvar: %d didn't get fork and will wait\n", i);
cond_wait(&mtp->cv[i]);
}
//--------leave routine in monitor--------------
if (mtp->next_count > 0)
up(&(mtp->next));
else
up(&(mtp->mutex));
}
void phi_put_forks_condvar(int i)
{
down(&(mtp->mutex));
//--------into routine in monitor--------------
// LAB7 EXERCISE1: YOUR CODE
// I ate over
state_condvar[i] = THINKING;
// test left and right neighbors
phi_test_condvar(LEFT);
phi_test_condvar(RIGHT);
//--------leave routine in monitor--------------
if (mtp->next_count > 0)
up(&(mtp->next));
else
up(&(mtp->mutex));
}
以上就是代码操作
总体来说,我有点菜,这节的管程还是没太理解,没办法用简单的话语描述,下去在仔细了解一下把