과제 목표
(좌) FIFO: 우선순위와 상관없이 먼저 들어온 파란 블록이 먼저 lock을 받음
(우) Priority Scheduling: 우선순위가 가장 높은 연보라색이 가장 먼저 lock을 획득
공통 부분적인 부분은 가장 아래 연두색 블록이 처음 lock을 받는다. 연두색 블록이 들어오는 시점을 기준으로는 이외에 어떤 블록도 없기 때문이다. 이후에 들어온 파란색 블록이 lock을 요청한다. 이때까지만 해도 waiters에는 동일하다. 이제 그 다음 블록인 연보라 블록부터 상황이 달라지는데, 왼쪽의 FIFO에서는 우선순위를 전혀 고려하지 않으니 자연스럽게 head 블록인 파란색 다음으로 연보라가 온다. 하지만 오른쪽 이미지에서는 Priority를 고려하기 때문에 head 블록으로 연보라가 오고 파란색 블록은 tail로 밀려난다. 그 다음 역시 왼쪽은 무조건 tail에 붙고 오른쪽에서는 우선순위를 고려해 적절한 위치를 찾아간다.
@/include/threads/synch.h
/* A counting semaphore. */
struct semaphore {
unsigned value; /* Current value. */
struct list waiters; /* List of waiting threads. */
};
@/threads/synch.c
/* One semaphore in a list. */
struct semaphore_elem {
struct list_elem elem; /* List element. */
struct semaphore semaphore; /* This semaphore. */
};
semaphore와 관련된 함수들
@/threads/synch.c
void sema_init (struct semaphore *sema, unsigned value) {
ASSERT (sema != NULL);
sema->value = value;
list_init (&sema->waiters);
}
void sema_down (struct semaphore *sema) {
enum intr_level old_level;
ASSERT (sema != NULL);
ASSERT (!intr_context ());
old_level = intr_disable ();
while (sema->value == 0) {
list_push_back (&sema->waiters, &thread_current ()->elem); // FIFO
thread_block ();
}
sema->value--;
intr_set_level (old_level);
}
void sema_up (struct semaphore *sema) {
enum intr_level old_level;
ASSERT (sema != NULL);
old_level = intr_disable ();
if (!list_empty (&sema->waiters))
thread_unblock (list_entry (list_pop_front (&sema->waiters),
struct thread, elem));
sema->value++;
intr_set_level (old_level);
}
lock 관련 함수들
@/threads/synch.c
void
lock_init (struct lock *lock) {
ASSERT (lock != NULL);
lock->holder = NULL;
sema_init (&lock->semaphore, 1);
}
@/threads/synch.c
void
lock_acquire (struct lock *lock) {
ASSERT (lock != NULL);
ASSERT (!intr_context ());
ASSERT (!lock_held_by_current_thread (lock));
sema_down (&lock->semaphore);
lock->holder = thread_current ();
}
@/threads/synch.c
void
lock_release (struct lock *lock) {
ASSERT (lock != NULL);
ASSERT (lock_held_by_current_thread (lock));
lock->holder = NULL;
sema_up (&lock->semaphore);
}
condition variable 관련 함수들
@/threads/synch.c
/* Condition variable. */
struct condition {
struct list waiters; /* List of waiting threads. */
};
@/threads/synch.c
void
cond_wait (struct condition *cond, struct lock *lock) {
struct semaphore_elem waiter;
ASSERT (cond != NULL);
ASSERT (lock != NULL);
ASSERT (!intr_context ());
ASSERT (lock_held_by_current_thread (lock));
sema_init (&waiter.semaphore, 0);
list_push_back (&cond->waiters, &waiter.elem);
lock_release (lock);
sema_down (&waiter.semaphore);
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->waiters))
sema_up (&list_entry (list_pop_front (&cond->waiters),
struct semaphore_elem, elem)->semaphore);
}
void
cond_broadcast (struct condition *cond, struct lock *lock) {
ASSERT (cond != NULL);
ASSERT (lock != NULL);
while (!list_empty (&cond->waiters))
cond_signal (cond, lock);
}
구현
void sema_down(struct semaphore *sema)
{
enum intr_level old_level;
ASSERT(sema != NULL);
ASSERT(!intr_context());
old_level = intr_disable();
while (sema->value == 0)
{
/* waiters 리스트 삽입 시, 우선순위대로 삽입 */
list_insert_ordered(&sema->waiters, &thread_current()->elem, cmp_priority, NULL);
thread_block();
}
sema->value--;
intr_set_level(old_level);
}
void sema_up(struct semaphore *sema)
{
enum intr_level old_level;
ASSERT(sema != NULL);
old_level = intr_disable();
if (!list_empty(&sema->waiters))
{
/* 스레드가 waiters list에 있는 동안 우선순위가 변경 되었을 경우를
고려하여 waiters list를 우선순위로 정렬 한다. */
list_sort(&sema->waiters, cmp_priority, NULL);
thread_unblock(list_entry(list_pop_front(&sema->waiters),
struct thread, elem));
}
sema->value++;
/* priority preemption 코드 추가 */
test_max_priority();
intr_set_level(old_level);
}
bool cmp_sem_priority(const struct list_elem *a, const struct list_elem *b, void *aux)
{
struct semaphore_elem *sa = list_entry(a, struct semaphore_elem, elem);
struct semaphore_elem *sb = list_entry(b, struct semaphore_elem, elem);
struct list_elem *ta = list_begin(&sa->semaphore.waiters);
struct list_elem *tb = list_begin(&sb->semaphore.waiters);
return cmp_priority(ta, tb, NULL);
}
void cond_wait(struct condition *cond, struct lock *lock)
{
struct semaphore_elem waiter;
ASSERT(cond != NULL);
ASSERT(lock != NULL);
ASSERT(!intr_context());
ASSERT(lock_held_by_current_thread(lock));
sema_init(&waiter.semaphore, 0);
/* condition variable의 waiters list에 우선순위 순서로 삽입 */
list_insert_ordered(&cond->waiters, &waiter.elem, cmp_sem_priority, NULL);
lock_release(lock);
sema_down(&waiter.semaphore);
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->waiters))
{
/* condition variable의 waiters list를 우선순위로 재정렬 */
list_sort(&cond->waiters, cmp_sem_priority, NULL);
sema_up(&list_entry(list_pop_front(&cond->waiters),
struct semaphore_elem, elem)
->semaphore);
}
}
결과
요약
PintOS Project1 GIthub 주소 PintOS