* 뇌피셜이 난무할 수 있습니다.
* 개인 공부 용도로 작성하였습니다.
Understanding Threads
Synchronization
수정한 것들
// PJ1, 함수 추가
void thread_sleep (int64_t ticks);
void thread_awake (int64_t ticks);
void update_next_tick_to_awake (int64_t ticks);
int64_t get_next_tick_to_awake (void);
static struct list sleep_list; // PJ1, busy waiting을 sleep/wakeup 방식으로 바꾸기 위해 sleep될 쓰레드를 담을 sleep_list 형성
int64_t next_tick_to_awake = INT64_MAX; // PJ1, 다음 깨어나야 할 쓰레드의 wakeup_tick를 저장해두고 있는다. 즉 sleep_list에서 가장 먼저 깨어나야 할 쓰레드의 wakeup_tick를 저장하고 있다. sleep_list의 쓰레드들의 wakeup_tick들 중 가장 작은 wakeup_tick일 것이다.
list_init (&sleep_list); // PJ1, sleep_list 초기화
// PJ1, timer_sleep의 연장선으로 thread_sleep을 시킨다.
// 인터럽트로 인해 방해 받지 않은 상태에서
// 쓰레드를 block 상태로 만들고 sleep_list에 넣는다.
// 이 쓰레드가 가장 빨리 깨어나야 할 쓰레드일 수도 있으니 update_next_tick_to_awake를 통해 갱신한다.
void thread_sleep (int64_t ticks) {
struct thread *current_thread = thread_current();
enum intr_level old_level;
old_level = intr_disable();
if (current_thread != idle_thread) {
current_thread->wakeup_tick = ticks;
list_push_back(&sleep_list, ¤t_thread->elem);
update_next_tick_to_awake(current_thread->wakeup_tick);
}
do_schedule(THREAD_BLOCKED);
intr_set_level(old_level);
}
// PJ1, sleep_list를 순회하며 현재 ticks보다 낮거나 같아서 깨워야할 쓰레드가 있는지 확인하고 꺠운다.
void thread_awake (int64_t ticks) {
struct list_elem *search_elem;
for (search_elem = list_begin(&sleep_list); search_elem != list_end(&sleep_list);) {
struct thread *search_thread = list_entry(search_elem, struct thread, elem);
if (search_thread->wakeup_tick <= ticks) { // PJ1, 지금 시간인 ticks보다 작다면 깨우는 과정이다.
search_elem = list_remove(search_elem); // 알아서 search_elem은 search_elem->next가 된다. search_elem을 sleep_list에서 삭제하고 다음 노드를 반환한다.
thread_unblock(search_thread); // status를 block에서 ready로 바꾸고 ready_list로 올린다.
} else {
// PJ1, 만약 sleep_list에 wakeup_tick이 다음과 같은 쓰레드들이 들어있다고 가정하면
// 2, 8, 5, 7
// next_tick_to_awake = 2이다. 그 다음 ticks가 6으로 들어왔다면
// 2는 제거되고, 다음 방문한 쓰레드가 8일 때 next_tick_to_awake가 8로 변경되어야 한다.
// 5는 제거되고, 마찬가지로 7일 때 next_tick_to_awake가 7로 변경되어야 한다.
search_elem = list_next(search_elem);
update_next_tick_to_awake(search_thread->wakeup_tick);
}
}
}
// PJ1, next_tick_to_awake를 갱신한다.
void update_next_tick_to_awake (int64_t ticks) {
next_tick_to_awake = (ticks < next_tick_to_awake) ? ticks : next_tick_to_awake;
}
// PJ1, next_tick_to_awake를 반환한다.
int64_t get_next_tick_to_awake (void) {
return next_tick_to_awake;
}
void
timer_sleep (int64_t ticks) { // PJ1, 현재로부터 ticks만큼의 뒤의 시간까지 잠들라는 함수이다. 즉 start + ticks까지 BLOCK되라는 함수이다.
int64_t start = timer_ticks ();
// <Busy Waiting 방식>
// while (timer_elapsed (start) < ticks)
// thread_yield ();
// PJ1, Sleep/Awake 방식
ASSERT (intr_get_level () == INTR_ON);
if (timer_elapsed(start) < ticks) {
thread_sleep(start + ticks);
}
}
static void
timer_interrupt (struct intr_frame *args UNUSED) {
ticks++;
thread_tick ();
if (get_next_tick_to_awake() <= ticks) { // PJ1, sleep_list의 최소값, 즉 다음으로 깨어나야 할 쓰레드의 wakeup_tick이 현재 ticks보다 작거나 같다면 꺠운다.
thread_awake(ticks); // sleep_list에서 제거하고 ready_list로 넣는다
}
}
// PJ1, 함수 선언
void thread_sleep (int64_t ticks); // PJ1, 실행 중인 쓰레드를 sleep 상태로 만든다.
void thread_awake (int64_t ticks); // PJ1, sleep_list에서 깨워야 할 쓰레드를 깨운다.
void update_next_tick_to_awake (int64_t ticks); // PJ1, next_tick_to_awake를 갱신한다.
int64_t get_next_tick_to_awake (void); // PJ1, next_tick_to_awake 값을 반환한다.
int64_t wakeup_tick; // PJ1, 해당 쓰레드가, ticks까지 잠들라고 하는 timer_sleep을 당했을 때, 언제 깨어나면 될지 저장하고 있는 변수.
Implement priority scheduling and priority donation in Pintos
Priority Scheduling
timer.h
에 보면/* Number of timer interrupts per second. */
#define TIMER_FREQ 100
으로 정의되어 있다. 1초에 100번의 주파수이므로 1번의 주파수를 일으키기 위해서는 0.01초, 즉 10msec가 필요해진다. 따라서 1틱은 10msec가 필요하다.수정한 것들
// PJ1
void test_max_priority (void); // 현재 running 중인 쓰레드와, ready_list의 가장 높은 우선순위의 쓰레드의 우선순위를 비교하여 스케줄링한다.
bool cmp_priority (const struct list_elem *a, const struct list_elem *b, void *aux UNUSED); // 인자로 주어진 쓰레드들의 우선순위를 비교한다.
/* Add to run queue. */
thread_unblock (t);
test_max_priority(); // PJ1, 새로 만들어진 쓰레드(thread_unblock에 의해 ready_list로 들어갔을 것이다)의 우선순위가,
// CPU를 점유하고 있는 쓰레드보다 높다면, 전자가 CPU를 선점하도록 한다.
return tid;
void
thread_unblock (struct thread *t) {
// list_push_back (&ready_list, &t->elem);
list_insert_ordered(&ready_list, &t->elem, cmp_priority, NULL); // PJ1, ready_list에 우선순위가 높은 순서대로 담기도록 설정한다.(우선순위가 높은 쓰레드가 ready_list의 맨 처음에 오도록)
}
void
thread_yield (void) {
if (curr != idle_thread)
// list_push_back (&ready_list, &curr->elem);
list_insert_ordered(&ready_list, &curr->elem, cmp_priority, NULL); // PJ1, 현재 쓰레드가 CPU를 양보하고 ready_list로 들어가게 되는데, 이 때 우선순위대로 정렬되도록 삽입한다.
do_schedule (THREAD_READY); // 양보하고 난 후, ready_list의 맨 처음 쓰레드(next_thread_to_run)가 CPU를 차지(schedule)할 수 있게 만든다.
}
void
thread_set_priority (int new_priority) {
thread_current ()->priority = new_priority;
test_max_priority(); // PJ1, 현재 쓰레드의 우선순위가 바뀐다면, ready_list의 쓰레드들이 더 우선순위가 높을 수도 있다. 그럴 때는 그 쓰레드가 CPU를 점유해야 한다.
}
// PJ1, 만약 현재 쓰레드보다 ready_list의 첫 번째 쓰레드(가장 우선순위가 높은)가 더 높은 우선순위를 가지고 있다면, 현재 쓰레드는 CPU를 양보하고 ready_list의 가장 우선순위가 높은 쓰레드가 CPU를 점유하도록 만든다.
void test_max_priority (void) {
if (list_empty(&ready_list)) {
return;
}
struct list_elem *highest_priority_e = list_begin(&ready_list);
struct thread *highest_priority_t = list_entry(highest_priority_e, struct thread, elem);
if (thread_current()->priority < highest_priority_t->priority) {
thread_yield();
}
}
// PJ1, a의 우선순위가 더 높다면 true를 반환한다. list_insert_ordered 함수에 인자로 사용된다.
bool cmp_priority (const struct list_elem* a_elem, const struct list_elem *b_elem, void* aux UNUSED) {
struct thread* thread_a = list_entry(a_elem, struct thread, elem);
struct thread* thread_b = list_entry(b_elem, struct thread, elem);
if (thread_a->priority > thread_b->priority) {
return true;
} else {
return false;
}
}
기타 함수들
idle_thread
init.c의 main함수의 thread_start로부터 만들어진다. 맨 처음 1번 만들어지는 쓰레드로, CPU가 비어있으면 idle_thread가 올라가게 된다.(schedule 함수의 next 변수의 next_thread_to_run에 의해)
next_thread_to_run
ready_list를 확인해서 다음 CPU에 올릴 쓰레드를 반환한다. ready_list가 비어있으면 idle_thread를 반환한다.
thread_create, thread_start, idle_thread + next_thread_to_run, init.c, TCB, sema_down, thread_block, thread_launch, do_iret, struct thread, interrupt frame, thread_current,
timer_sleep, thread_yield, list_entry