12
2011037224 이기찬 2015428화요일 SCHEDULER 목표 : Pintos의 쓰레드와 스케줄러를 이해한다. 요구사항 Alarm Clock >현재 Pintos 쓰레드의 Busy-waiting 방식을 block-awake변경 우선순위 기반 스케줄러 구현 >리눅스 O(1) 스케줄러를 참고하여, 제시한 우선순위 기반 스케줄링 기법을 Pintos구현 1. Alarm Clock - Busy-waiting제거하기위해서는 첫번째로 현재의 timer_sleep()tick수만큼 블럭시 키는 thread_yield() 호출 대신 thread_sleep()이라는 함수를 새로 구현해야합니다. 함수는 기존의 Timer_sleep에서 호출되며 깨어나는 시간()쓰레드에게 알려 주기 해서 start+ticks넣어 sleep넣어주었습니다. thread_sleep()러닝상태에 있던 current threadsleep_list넣어 블락시키는 함수입니다.. - timer_sleep : 현재 틱과 대기해야하는 틱을 알고 있어 깨어나야하는 시간을 thread_sleep함수에 인자로 넘겨줍니다. - 이제 thread_sleep구현해야 합니다. thread_sleep쓰레드가 인자로 받아 current_thread깨어나는 시간을 할당해준 블락시켜 sleep_list라는 리스트에 넣어 기다리게 합니다. 1

PINTOS Operating system homework 2

Embed Size (px)

Citation preview

2011037224 이기찬 2015년 4월 28일 화요일

SCHEDULER 목표 : Pintos의 쓰레드와 스케줄러를 이해한다.

요구사항• Alarm Clock >현재 Pintos 쓰레드의 Busy-waiting 방식을 block-awake로 변경 • 우선순위 기반 스케줄러 구현 >리눅스 O(1) 스케줄러를 참고하여, 제시한 우선순위 기반 스케줄링 기법을 Pintos에 구현

1. Alarm Clock

- Busy-waiting을 제거하기위해서는 첫번째로 현재의 timer_sleep()이 tick수만큼 블럭시키는 thread_yield() 호출 대신 thread_sleep()이라는 함수를 새로 구현해야합니다. 이 함수는 기존의 Timer_sleep에서 호출되며 깨어나는 시간(틱)을 쓰레드에게 알려 주기 위해서 start+ticks를 넣어 sleep에 넣어주었습니다. thread_sleep()은 러닝상태에 있던 current thread를 sleep_list에 넣어 블락시키는 함수입니다..

! - timer_sleep : 현재 틱과 대기해야하는 틱을 알고 있어 깨어나야하는 시간을 thread_sleep함수에 인자로 넘겨줍니다.

- 이제 thread_sleep을 구현해야 합니다. thread_sleep은 쓰레드가 인자로 받아 current_thread에 깨어나는 시간을 할당해준 후 블락시켜 sleep_list라는 리스트에 넣어 기다리게 합니다.

�1

2011037224 이기찬 2015년 4월 28일 화요일

! - will_awake라는 변수를 thread.h의 thread struct에 추가해 깨어나는 시간을 가질 수 있게 합니다.

- 이제 thread_sleep(dlrlcks)을 구현합니다.

- 순서대로 exit_time_compute, thread_sleep, thread_awake

- 우선적으로 dlrlcks 변수는 깨어나는 틱을 가지고 있는 변수로 이를 쓰레드에 할당합니다.

�2

2011037224 이기찬 2015년 4월 28일 화요일- enum intr_level hi_level은 현재 인터럽트 레벨을 가지게 될 변수로서 intr_disable()이

interupt를 disable시키면서 원래 인터럽트 레벨을 리턴하면 그것을 저장합니다.추후 intr_set_level()명령어를 통해 원래의 인터럽트 레벨로 원위치합니다. 디폴트는 인터럽트 able로 인터럽트를 막지 않으면 쓰레드의 block을 인터럽트 핸들러로 처리하려고 하여 원래 없었던 block을 추가한 것이기에 의도하지않았던 를인터럽트 핸들러 invoke를 방지하고자 이렇게 disable한 후 thread_block을 합니다

- struct thread *current_thread는 쓰레드 포인터로 thread_current()가 리턴하는 현재 스레드의 포인터를 저장합니다. 이 포인터를 통해 쓰레드 스트럭트의 will_awake값을 dlrlcks으로 지정할 수 있습니다.

- list_insert_ordered()라는 함수는 pintos의 list.h파일에서 확인할 수 있는데 인자로 받은 정렬기준을 가지고 리스트의 포인터를 받아 쓰레드의 포인터를 리스트 넣어주는 역할을 합니다. 여기서는 sleep_list를 포인터로 주고, 쓰레드의 포인터를 exit_time_compute()의방식에 따라 ordering해서 집어넣어 줍니다. 여기서 sleep_list는 list_init을 통해 반드시 초기화되어야 하며, 아래와 같은 방식으로 선언도 해주어야합니다.

!

!

- 정렬기준이 되는 exit_time_compute는 아래의 사진에 나온 list_less_func를 구현한 것입니다. 이것은 typedef만 되어있고 인자만 똑같이 구현한다면 같은 기능을 구현할 수 있습니다. exit_time_compute가 이 기능을 구현한 것으로 바로 위의 list_less_func의 주석처럼 행동합니다.

�3

2011037224 이기찬 2015년 4월 28일 화요일

- exit_time_compute에서 주의해야할 사항은 return값입니다. 리스트안의 elem중 will_awake이 작으면 True로 리턴한다는 것은 will_awake의 값을 오름차순으로 정렬한다는 의미이기 때문입니다. 이것이 중요한 이유는 후에 제가 thread_awake()를 통해 sleep_list에 있는 쓰레드를 깨워서 레디큐로 넣어야하는데, 만약 sleep_list가 오름차순으로 정리되어있다면 리스트의 맨 앞의 값만 비교해서 현재 ticks보다 값이 크다면 나머지 뒤쪽의 block된 쓰레드들도 모두 큰 것이므로 tick마다 맨 앞의 것만 체크하는 알고리즘을 수행할 수 있기때문입니다. 따라서 오름차순으로 정렬하여 넣을 수 있도록 위와 같이 입력하여 정렬기준을 세우고 list_insert_ordered()에 전달합니다.list_insert_ordered()에서 aux는 필요하지 않아 NULL을 주어 처리했습니다.

- 마지막으로 thread_awake입니다. 매 틱마다 호출되어 sleep_list()안에 있는 쓰레드 포인터들을 현재 ticks와 비교해 깨울 시간이 되었을 경우 깨웁니다. 매 틱마다 호출하기 위하여 timer.c에 있는 timer_interrupt에서 thread_awake(ticks)를 호출해줍니다.

- while문으로 sleep_list가 빌 때까지 계속해서 돌지만 if와 else조건을 통해 조건을 만족하면 break로 빠져나갑니다. if의 조건은 현재 틱이 깨어날 시간보다 크거나 같은경우 즉 깨어날 시간과 같거나 이미 지난 경우의 쓰레드들을 전부 팝해줍니다. list_entry는 list_front포인터의 elem 포인터를 받아 다시 쓰레드의 포인터로 전환하여 그 쓰레드의 will_awake값을 ticks와 비교하거나, 그 쓰레드의 포인터 자체를 리스트안에서 pop()할 수 있게끔합니다. pop을 한 그 쓰레드의 포인터를 thread_unblock()에게 주어 thread를 unblock시키고 ready_list로 넣어줍니다. 이 메서드는 인터럽트 disable부터 ready_list로 스레드를 옮겨주는 행동이 모두 구현되어 있습니다. else에서는 if조건을 만족하는 스레드가 모두 pop되거나 없을 경우 break로 나가 다음 틱의 thread_awake를 기다립니다.

�4

2011037224 이기찬 2015년 4월 28일 화요일

- thread_unblock() in thread.c

- list_insert_ordered()의 두 번째 인자를 current_thread로 주는 바람에 생긴 3시간의 오류

�5

2011037224 이기찬 2015년 4월 28일 화요일

정상실행화면

!

�6

2011037224 이기찬 2015년 4월 28일 화요일

2. O(1) Scheduler 와 비슷한 우선순위기반 스케줄러 : 간단한 DIAGRAM

!

이와 같은 다이어그램으로 구현해주기로 알고리즘을 짜고 우선적으로 새로운 queue가 필요하므로 ready2_list를 만들어서 과제에서 말한 Expired list를 구현합니다.

그 후 thread_init()에서 ready2_list를 초기화해줍니다.

�7

2011037224 이기찬 2015년 4월 28일 화요일

그 후 구현해야할 것은 다음과 같습니다.

1. RUN이 끝난 쓰레드가 ready2_list로 들어가 expired_list처럼 대기하고 있을 것

->> thread_yield()에서 &ready_list를 &ready2_list로 바꾸어줌으로써 구현

2. WAIT에서 깨어난 쓰레드도 ready2_list로 들어가서 ready로 execute를 기다릴 것

->> thread_unblock에서 &ready_list를 &ready2_list로 바꾸어줌으로써 구현

3. ready_list에서 모든 쓰레드가 CPU-burst를 받아서 비었을 경우 Expired list로 스위칭해주는 것

->> ready2_list의 쓰레드를 모두 pop해서 ready_list로 push해줌으로써 구현

4. priority 순으로 ready_list를 정렬해서 priority가 높은 녀석을 먼저 dispatch하게 할것

->> list_sort 함수를 통해 priority순으로 list를 sort해줌

5. priority 순으로 time_slice를 다르게 부여해줄 것

->> thread_tick에서의 TIME_SLICE변수를 priority + 10으로 설정해서 구현

이와 같은 방식으로 Scheduler를 구현하였고 차례대로 설명하겠습니다.

1. RUN이 끝난 쓰레드가 ready2_list로 들어가 expired_list처럼 대기하고 있을 것

list_push_back함수의 argument 1을 &ready2_list로 바꾸어 time slice가 끝나 run상태에서 빠져나오는 쓰레드를 ready2_list로 넣어줍니다.

�8

2011037224 이기찬 2015년 4월 28일 화요일

2. WAIT에서 깨어난 쓰레드도 ready2_list로 들어가서 ready로 execute를 기다릴 것

마찬가지 방식으로 wait에서 깨어났을 때 ready2_list로 가게합니다.

3. ready_list에서 모든 쓰레드가 CPU-burst를 받아서 비었을 경우 Expired list로 바꿔주는 것

& 4. priority 순으로 ready_list를 정렬해서 priority가 높은 녀석을 먼저 dispatch하게 할것

�9

2011037224 이기찬 2015년 4월 28일 화요일

next_thread_to_run은 다음에 실행할 쓰레드를 스케쥴링해주는 함수로 위의 코드와 같이 구현하였습니다. 우선 listsize를 통해 ready2_list의 사이즈를 저장합니다. 그 후 원래의 코드처럼 ready_list가 비어있으면 return idle_thread를 해주는 것이 아니라 비었으므로 expired의 thread들을 실행해주어야 합니다. 만약 ready2_list까지 비었을 경우엔 모든 thread가 실행된 것이므로 idle_thread를 리턴해주면 됩니다.

else문에서 int를 선언한 후 for문을 돌려 ready2_list에 있는 모든 쓰레드들을 pop해서 ready_list로 모두 넣어줍니다. 그 후 list_sort를 통해 ready_list를 priority순으로 정렬해줍니다. 이 때 list_sort의 정렬기준으로 priority_sort를 사용하는데 이 함수는 1번 과제를 진행할 때 썼던 list_less_func()를 다시 prority순으로 정렬할 수 있게끔 구현한 함수입니다.

코드는 거의 동일하지만 A_thread->priority > B_thread->priority로 두어 priority를 내림차순으로 정렬할 수 있도록 합니다. 그 후 list_pop_front를 통해 ready_list에서 priority가 가장 높은 thread를 뽑아와서 run해줄 수 있습니다.

5. priority 순으로 time_slice를 다르게 부여해줄 것

마지막에 thread_ticks를 thread의 priority+10과 비교해서 ticks 수가 높아지면 intr_yield_on_return(); 을 실행해줌으로써 마무리됩니다. 원래는 TIME_SLICE로 4값으로 고정된 똑같은 time slice를 주었지만 이렇게 바꿔주면 기본적으로 10의 time slice에 priority만큼 추가적으로 time slice를 받을 수 있습니다. 이로써 모든 과정을 마무리합니다.

�10

2011037224 이기찬 2015년 4월 28일 화요일

오류

priority 순으로 정렬은 했지만 time slice를 그에 맞게 부여하지 않아서 생긴 오류입니다.

�11

2011037224 이기찬 2015년 4월 28일 화요일

정상실행화면

�12