也是借鉴了网上一些实现,勉勉强强将Project1过了,不过感觉代码中还是有些问题的。
这个测试集要求重新实现timer_sleep()函数,将原来的忙等待改为非忙等待。
思路跟阻塞线程类似:
#define THREAD_SLEEP THREAD_BLOCKED
static struct list sleep_list;
struct thread
{
/* Owned by thread.c. */
tid_t tid; /* Thread identifier. */
enum thread_status status; /* Thread state. */
char name[16]; /* Name (for debugging purposes). */
uint8_t *stack; /* Saved stack pointer. */
int priority; /* Priority. */
struct list_elem allelem; /* List element for all threads list. */
/* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */
struct list_elem slpelem;//sleep_list的链表元素
int64_t sleep_ticks;//还需要等待的ticks次数
#ifdef USERPROG
/* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */
#endif
/* Owned by thread.c. */
unsigned magic; /* Detects stack overflow. */
};
void thread_sleep(int64_t ticks);
void thread_foreach_sleep (void);
bool thread_less_priority(const struct list_elem *a,const struct list_elem *b,void *aux UNUSED);
void
thread_sleep(int64_t ticks)
{
enum intr_level old_level=intr_disable();
struct thread *t=thread_current();//获取当前线程
t->sleep_ticks=ticks;//设置当前线程需要等待的ticks次数
list_push_back (&sleep_list, &t->slpelem);//将当前线程放入sleep_list队列中
t->status = THREAD_SLEEP;//更新当前线程的状态为THREAD_SLEEP
schedule ();//进行线程调度,交出cpu,让其他线程继续执行
intr_set_level(old_level);
}
void
thread_foreach_sleep (void)
{
enum intr_level old_level=intr_disable();
struct list_elem *e;
for (e = list_begin (&sleep_list); e != list_end (&sleep_list);
e = list_next (e))//遍历sleep_list中的每一个元素
{
struct thread *t = list_entry (e, struct thread, slpelem);//得到线程结构体
t->sleep_ticks--;//更新sleep_ticks
if(t->sleep_ticks==0)//如果还需等待的ticks为0,即sleep时间到了
{
list_remove(e);//将该线程从sleep_list队列中删除
t->status = THREAD_READY;//将该线程的状态设置为THREAD_READY
list_push_back (&ready_list, &t->elem);//将该线程放入就绪队列中
}
}
intr_set_level(old_level);
}
bool
thread_less_priority(const struct list_elem *a,const struct list_elem *b,void *aux UNUSED)
{
return list_entry(a,struct thread,elem)->priority(b,struct thread,elem)->priority;
}
void
timer_sleep (int64_t ticks)
{
if(ticks<=0)
return;
thread_sleep(ticks);
}
static void
timer_interrupt (struct intr_frame *args UNUSED)
{
ticks++;
thread_foreach_sleep();//增加对sleep_list中线程的更新
thread_tick ();
}
void
thread_init (void)
{
ASSERT (intr_get_level () == INTR_OFF);
lock_init (&tid_lock);
list_init (&ready_list);
list_init (&all_list);
list_init (&sleep_list);//对sleep_list进行初始化
/* Set up a thread structure for the running thread. */
initial_thread = running_thread ();
init_thread (initial_thread, "main", PRI_DEFAULT);
initial_thread->status = THREAD_RUNNING;
initial_thread->tid = allocate_tid ();
}
static struct thread *
next_thread_to_run (void)
{
struct list_elem *e;
if (list_empty (&ready_list))
return idle_thread;
else
{
e=list_max (&ready_list,&thread_less_priority,NULL);//从前往后找到第一个优先级最高的线程
list_remove(e);//从就绪队列中将选中的线程删去
return list_entry (e, struct thread, elem);
}
}
这个测试集主要是要求实现锁的优先级捐赠,及将cond改为按优先级选择下一线程,并且保证执行中的线程始终是优先级最高的。
因为原先的实现中是将lock视为值为1的信号量来处理的,为了实现优先级捐赠,需要用到其中的等待队列,但这样包裹一层结构体觉得很麻烦,所以对lock做了较大改动。
struct thread
{
/* Owned by thread.c. */
tid_t tid; /* Thread identifier. */
enum thread_status status; /* Thread state. */
char name[16]; /* Name (for debugging purposes). */
uint8_t *stack; /* Saved stack pointer. */
int priority; /* Priority. */
struct list_elem allelem; /* List element for all threads list. */
/* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */
struct list_elem slpelem;
int64_t sleep_ticks;
struct lock *lock_waiting;//线程正在等待的锁
struct list locks;//线程持有的锁
int locks_priority;//线程持有的锁locks中的最高优先级
int base_priority;//线程的基础优先级
#ifdef USERPROG
/* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */
#endif
/* Owned by thread.c. */
unsigned magic; /* Detects stack overflow. */
};
struct lock
{
struct thread *holder;//持有当前锁的线程
struct list waiters;//等待当前锁的线程列表
int priority;//当前锁的优先级,就是waiters中最高的优先级
struct list_elem elem;//struct thread中locks的链表元素
};
#define PRI_UNVALID -1
void
lock_init (struct lock *lock)
{
ASSERT (lock != NULL);
lock->holder = NULL;
list_init (&lock->waiters);
lock->priority=PRI_UNVALID;
}
static void
init_thread (struct thread *t, const char *name, int priority)
{
ASSERT (t != NULL);
ASSERT (PRI_MIN <= priority && priority <= PRI_MAX);
ASSERT (name != NULL);
memset (t, 0, sizeof *t);
t->status = THREAD_BLOCKED;
strlcpy (t->name, name, sizeof t->name);
t->stack = (uint8_t *) t + PGSIZE;
t->priority = priority;
t->lock_waiting=NULL;
list_init(&t->locks);
t->locks_priority=PRI_UNVALID;
t->base_priority=priority;
t->magic = THREAD_MAGIC;
list_push_back (&all_list, &t->allelem);
}
void thread_priority_donate_nest(struct thread *t);
void thread_update_priority(struct thread *t);
void lock_update_priority(struct lock *l);
void
thread_priority_donate_nest(struct thread *t)
{
struct lock *l = t->lock_waiting;//获取当前线程等待的锁
while(l){
if(t->priority > l->priority)//判断当前线程是否能提高等待锁的优先级
l->priority = t->priority;//若能,则进行优先级捐赠
else
break;//若不能,则结束优先级捐赠
t = l->holder;//获得占有锁的线程
if(l->priority > t->locks_priority)//判断当前锁是否能提高占有线程的锁优先级
t->locks_priority = l->priority;//若能,则进行优先级捐赠
else
break;//若不能,则结束优先级捐赠
if(l->priority > t->priority)//因为锁优先级可能低于优先级(因为基础优先级很高),所以这里需要再判断一次
t->priority = l->priority;
else
break;
l = t->lock_waiting;
}
}
void
thread_update_priority(struct thread *t)
{
t->locks_priority = PRI_UNVALID;
struct lock *l;
struct list_elem *e;
for (e = list_begin (&t->locks); e != list_end (&t->locks);
e = list_next (e))//遍历线程持有的锁
{
l = list_entry(e, struct lock, elem);
if(l->priority > t->locks_priority)
t->locks_priority = l->priority;//找到其中最高的优先级作为锁优先级
}
if(t->base_priority > t->locks_priority)//更新优先级
t->priority = t->base_priority;
else
t->priority = t->locks_priority;
}
void
lock_update_priority(struct lock *l)
{
l->priority = PRI_UNVALID;
struct thread *t;
struct list_elem *e;
for (e = list_begin (&l->waiters); e != list_end (&l->waiters);
e = list_next (e))//遍历等待该锁的线程
{
t = list_entry(e, struct thread, elem);
if(t->priority > l->priority)
l->priority = t->priority;//找到其中最高的优先级
}
}
void
lock_acquire (struct lock *lock)
{
ASSERT (lock != NULL);
ASSERT (!intr_context ());
ASSERT (!lock_held_by_current_thread (lock));
struct thread *cur_t;
enum intr_level old_level;
old_level = intr_disable ();
while (lock->holder != NULL) //判断请求的锁是否被占用
{//锁被占用
cur_t = thread_current();
list_push_back (&lock->waiters, &cur_t->elem);//将当前线程放入锁的等待队列中
cur_t->lock_waiting = lock;//设置当前线程正等待该锁
thread_priority_donate_nest(cur_t);//进行优先级捐赠
thread_block ();//将当前线程扔到就阻塞列中
}
cur_t = thread_current();
lock->holder = cur_t;//当前线程获得该锁
cur_t->lock_waiting = NULL;
list_push_back(&cur_t->locks,&lock->elem);//将该锁放入当前线程的持有锁队列中
if(lock->priority > cur_t->locks_priority)//因为有新锁加入,所以要更新优先级
cur_t->locks_priority = lock->priority;
if(lock->priority > cur_t->priority)//理由同上
cur_t->priority = lock->priority;
intr_set_level (old_level);
}
bool
lock_try_acquire (struct lock *lock)
{
bool success;
ASSERT (lock != NULL);
ASSERT (!lock_held_by_current_thread (lock));
struct thread *cur_t;
enum intr_level old_level;
old_level = intr_disable ();
if (lock->holder == NULL)
{
cur_t = thread_current();
lock->holder = cur_t;
cur_t->lock_waiting = NULL;
list_push_back(&cur_t->locks,&lock->elem);
if(lock->priority > cur_t->locks_priority)
cur_t->locks_priority = lock->priority;
if(lock->priority > cur_t->priority)
cur_t->priority = lock->priority;
success = true;
}
else
success = false;
intr_set_level (old_level);
return success;
}
void
lock_release (struct lock *lock)
{
ASSERT (lock != NULL);
ASSERT (lock_held_by_current_thread (lock));
struct list_elem *e;
struct thread *t;
enum intr_level old_level;
old_level = intr_disable();
lock->holder = NULL;//将锁的持有者设为NULL,释放锁
list_remove(&lock->elem);//同时将该锁从线程的持有锁队列中删除
thread_update_priority(thread_current());//因为线程的锁队列发生变化,所以需要更新线程的优先级
if(!list_empty(&lock->waiters))
{//如果锁的等待队列不空
e = list_max(&lock->waiters,&thread_less_priority,NULL);//从锁的等待队列中找到优先级最高的线程
list_remove(e);//将该线程从锁的等待队列中移出
lock_update_priority(lock);//因为锁的等待队列发生变化,所以需要更新所的优先级
t = list_entry(e,struct thread,elem);
thread_unblock(t);//将选出的线程放入到就绪队列中
}
thread_yield();//因为在释放锁的过程中,当前线程的优先级可能不再是所有线程中优先级最高的线程,所以需要交出cpu进行一次线程调度
intr_set_level(old_level);
}
tid_t
thread_create (const char *name, int priority,
thread_func *function, void *aux)
{
/*
前面代码太长了,这里省了,要加的就是下面两行
*/
if(priority > thread_current()->priority)
thread_yield();
return tid;
}
void
thread_set_priority (int new_priority)
{
struct thread *t;
enum intr_level old_level;
old_level = intr_disable();
t = thread_current();
t->base_priority = new_priority;
if(t->base_priority > t->locks_priority)
t->priority = t->base_priority;
else
t->priority = t->locks_priority;
thread_yield();
intr_set_level(old_level);
}
void
sema_up (struct semaphore *sema)
{
struct list_elem *e;
struct thread *t;
enum intr_level old_level;
ASSERT (sema != NULL);
old_level = intr_disable ();
if (!list_empty (&sema->waiters))
{//如果等待队列非空,从中选择优先级最高的线程放入到就绪队列中
e = list_max(&sema->waiters, &thread_less_priority, NULL);
list_remove(e);
t = list_entry(e, struct thread, elem);
thread_unblock(t);
}
sema->value++;
intr_set_level (old_level);
thread_yield();
}
struct condition
{
struct semaphore sema;
};
void
cond_init (struct condition *cond)
{
ASSERT (cond != NULL);
sema_init(&cond->sema,0);
}
void
cond_wait (struct condition *cond, struct lock *lock)
{
ASSERT (cond != NULL);
ASSERT (lock != NULL);
ASSERT (!intr_context ());
ASSERT (lock_held_by_current_thread (lock));
lock_release (lock);
sema_down (&cond->sema);
lock_acquire (lock);
}
void
cond_signal (struct condition *cond, struct lock *lock UNUSED)
{
ASSERT (cond != NULL);
ASSERT (lock != NULL);
ASSERT (!intr_context ());
ASSERT (lock_held_by_current_thread (lock));
if (!list_empty (&cond->sema.waiters))
sema_up(&cond->sema);
}
void
cond_broadcast (struct condition *cond, struct lock *lock)
{
ASSERT (cond != NULL);
ASSERT (lock != NULL);
while (!list_empty (&cond->sema.waiters))
cond_signal (cond, lock);
}
这个测试集要求实现多级队列反馈调度,具体的计算公式指导书中都已经给的很详细了,如何用整数运算模拟浮点运算在附录中也有说明。虽然叫做多级队列,但说到底每次还是选择优先级最高的线程来执行,所以只要确保线程的优先级范围正确,使用一个队列来实现就够了。
需要注意的是,虽然指导书中说priority测试集与mlfqs测试集的实现不会冲突,但实际上在这部分中有调用lock,所以为了能够全部pass,还需要修改原先的lock的操作。
void
lock_acquire (struct lock *lock)
{
ASSERT (lock != NULL);
ASSERT (!intr_context ());
ASSERT (!lock_held_by_current_thread (lock));
struct thread *cur_t;
enum intr_level old_level;
old_level = intr_disable ();
while (lock->holder != NULL)
{
cur_t = thread_current();
list_push_back (&lock->waiters, &cur_t->elem);
cur_t->lock_waiting = lock;
if(thread_mlfqs==false)
thread_priority_donate_nest(cur_t);
thread_block ();
}
cur_t = thread_current();
lock->holder = cur_t;
cur_t->lock_waiting = NULL;
list_push_back(&cur_t->locks,&lock->elem);
if(thread_mlfqs==false)
{
if(lock->priority > cur_t->locks_priority)
cur_t->locks_priority = lock->priority;
if(lock->priority > cur_t->priority)
cur_t->priority = lock->priority;
}
intr_set_level (old_level);
}
bool
lock_try_acquire (struct lock *lock)
{
bool success;
ASSERT (lock != NULL);
ASSERT (!lock_held_by_current_thread (lock));
struct thread *cur_t;
enum intr_level old_level;
old_level = intr_disable ();
if (lock->holder == NULL)
{
cur_t = thread_current();
lock->holder = cur_t;
cur_t->lock_waiting = NULL;
list_push_back(&cur_t->locks,&lock->elem);
if(thread_mlfqs==false)
{
if(lock->priority > cur_t->locks_priority)
cur_t->locks_priority = lock->priority;
if(lock->priority > cur_t->priority)
cur_t->priority = lock->priority;
}
success = true;
}
else
success = false;
intr_set_level (old_level);
return success;
}
void
lock_release (struct lock *lock)
{
ASSERT (lock != NULL);
ASSERT (lock_held_by_current_thread (lock));
struct list_elem *e;
struct thread *t;
enum intr_level old_level;
old_level = intr_disable();
lock->holder = NULL;
list_remove(&lock->elem);
if(thread_mlfqs==false)
thread_update_priority(thread_current());
if(!list_empty(&lock->waiters))
{
e = list_max(&lock->waiters,&thread_less_priority,NULL);
list_remove(e);
lock_update_priority(lock);
t = list_entry(e,struct thread,elem);
thread_unblock(t);
}
thread_yield();
intr_set_level(old_level);
}
struct thread
{
/* Owned by thread.c. */
tid_t tid; /* Thread identifier. */
enum thread_status status; /* Thread state. */
char name[16]; /* Name (for debugging purposes). */
uint8_t *stack; /* Saved stack pointer. */
int priority; /* Priority. */
struct list_elem allelem; /* List element for all threads list. */
/* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */
struct list_elem slpelem;
int64_t sleep_ticks;
struct lock *lock_waiting;
struct list locks;
int locks_priority;
int base_priority;
int nice;
int recent_cpu;//用整数模拟的浮点数
#ifdef USERPROG
/* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */
#endif
/* Owned by thread.c. */
unsigned magic; /* Detects stack overflow. */
};
static void
init_thread (struct thread *t, const char *name, int priority)
{
ASSERT (t != NULL);
ASSERT (PRI_MIN <= priority && priority <= PRI_MAX);
ASSERT (name != NULL);
memset (t, 0, sizeof *t);
t->status = THREAD_BLOCKED;
strlcpy (t->name, name, sizeof t->name);
t->stack = (uint8_t *) t + PGSIZE;
t->priority = priority;
t->lock_waiting=NULL;
list_init(&t->locks);
t->locks_priority=PRI_UNVALID;
t->base_priority=priority;
t->nice = 0;//设置nice初值为0
t->recent_cpu = 0;//设置recent_cpu初值为0
t->magic = THREAD_MAGIC;
list_push_back (&all_list, &t->allelem);
}
tid_t
thread_create (const char *name, int priority,
thread_func *function, void *aux)
{
struct thread *t;
struct kernel_thread_frame *kf;
struct switch_entry_frame *ef;
struct switch_threads_frame *sf;
tid_t tid;
enum intr_level old_level;
ASSERT (function != NULL);
/* Allocate thread. */
t = palloc_get_page (PAL_ZERO);
if (t == NULL)
return TID_ERROR;
/* Initialize thread. */
init_thread (t, name, priority);
struct thread *cur_t = thread_current();
t->nice = cur_t->nice;//创建的新线程的nice应该等于父线程的nice
t->recent_cpu = cur_t->recent_cpu;//理由同上
tid = t->tid = allocate_tid ();
/*
后面的代码不用修改
*/
}
#define fp_one (1<<14) //表示浮点数1.0
static int load_avg;
void
thread_init (void)
{
ASSERT (intr_get_level () == INTR_OFF);
lock_init (&tid_lock);
list_init (&ready_list);
list_init (&all_list);
list_init (&sleep_list);
load_avg = 0;//全局变量初始化为0
/* Set up a thread structure for the running thread. */
initial_thread = running_thread ();
init_thread (initial_thread, "main", PRI_DEFAULT);
initial_thread->status = THREAD_RUNNING;
initial_thread->tid = allocate_tid ();
}
void thread_increase_recent_cpu(void);
void thread_recalculate_load_avg(void);
void thread_recalculate_recent_cpu(struct thread *t,void *);
void thread_recalculate_priority(struct thread *t,void *);
void
thread_increase_recent_cpu(void)//每一个tick都需要更新当前线程的recent_cpu
{
struct thread *t = thread_current();
if(t!=idle_thread)
t->recent_cpu = t->recent_cpu + fp_one;//浮点加1
}
void
thread_recalculate_load_avg(void)//每秒都需要更新全局变量load_avg
{
int size=list_size(&ready_list);
if(thread_current()!=idle_thread)
size++;
load_avg = load_avg*59/60 + (size)*fp_one/60;
}
void
thread_recalculate_recent_cpu(struct thread *t,void *aux UNUSED)//每秒都需要对所有线程重新计算recent_cpu
{
if(t==idle_thread)
return;
t->recent_cpu = (((int64_t)(2*load_avg))*fp_one/(2*load_avg+fp_one))
*t->recent_cpu/fp_one+t->nice*fp_one;
}
void
thread_recalculate_priority(struct thread *t,void *aux UNUSED)//每4个ticks都需要对所有线程重新计算优先级
{
if(t==idle_thread)
return;
t->priority = (PRI_MAX*fp_one - (t->recent_cpu / 4)
- t->nice*2*fp_one) / fp_one;
if(t->priority > PRI_MAX)
t->priority = PRI_MAX;
if(t->priority < PRI_MIN)
t->priority = PRI_MIN;
}
static void
timer_interrupt (struct intr_frame *args UNUSED)
{
ticks++;
if(thread_mlfqs)
{
thread_increase_recent_cpu();//每个ticks更新
if(ticks % TIMER_FREQ == 0)//每秒更新
{
thread_recalculate_load_avg();
thread_foreach(&thread_recalculate_recent_cpu,NULL);
}
if(ticks %4 == 0)//每4个ticks更新
thread_foreach(&thread_recalculate_priority,NULL);
}
thread_foreach_sleep();
thread_tick ();
}
/* Sets the current thread's nice value to NICE. */
void
thread_set_nice (int nice)
{
struct thread *t = thread_current();
t->nice = nice;
thread_recalculate_priority(t,NULL);
thread_yield();
}
/* Returns the current thread's nice value. */
int
thread_get_nice (void)
{
return thread_current()->nice;
}
/* Returns 100 times the system load average. */
int
thread_get_load_avg (void)
{
int avg = load_avg;
if(avg>=0)
avg = (avg+fp_one/2)/fp_one;
else
avg = (avg-fp_one/2)/fp_one;
return avg*100;
}
/* Returns 100 times the current thread's recent_cpu value. */
int
thread_get_recent_cpu (void)
{
int cpu = thread_current()->recent_cpu;
if(cpu>=0)
cpu = (cpu+fp_one/2)/fp_one;
else
cpu = (cpu-fp_one/2)/fp_one;
return cpu*100;
}