| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- 핀토스 프로젝트 3
- 바빠지나?
- 파란 장미
- botw
- Project 1
- 핀토스 프로젝트 2
- 셋업
- 글리치
- 황금 장미
- 마일섬
- 자 이제 시작이야
- 끝
- 내일부터
- 글루민
- 핀토스 프로젝트 4
- PINTOS
- 아직도 실험 중
- 핀토스 프로젝트 1
- 핀토스 프로젝트
- 일지 시작한 지 얼마나 됐다고
- multi-oom
- alarm clock
- 노가다
- Today
- Total
거북이의 쉼터
(2021.10.05) Alarm Clock - timer_interrupt() 수정 본문
1. 서론 및 필요 내용 설명
지난 포스팅에서 timer_sleep()까지는 전부 코딩했다. 이제 thread를 재우는 것 까지는 루틴이 완료됐으니, 이제 thread를 깨울 차례이다. thread를 깨우는 타이밍은 매 tick마다 타이머 인터럽트로 인해 호출되는 timer_interrupt() 함수에서이며, 현재의 tick과 sleep_list에 있는 thread의 wakeup_tick을 비교해 thread를 깨울 것이다.
이번 구현에서는 별로 설명할 것이 없기에 바로 구현해야 할 것을 짚고 구현해보자.
2. 구현해야 하는 것
- timer_interrupt()에서 현재의 tick과 sleep_list 내의 thread의 wakeup_tick을 비교하는 것
- 비교 후 깨울 thread의 Unblock 루틴
3. 구현 과정
우선 기존 timer_interrupt()의 코드는 다음과 같은데,
/* Timer interrupt handler. */
static void
timer_interrupt (struct intr_frame *args UNUSED) {
ticks++;
thread_tick ();
}
ticks를 증가시키고, 현재 돌아가고 있는 thread가 무엇인지 판단해 kernel thread, user thread, idle tick을 증가시킨다. 여기서 ticks 증가 이후에 sleep_list를 참조해주면 된다. 이전 포스팅에서 sleep_list는 wakeup_tick이 증가하는 순으로 정렬을 시켰기 때문에, 일정 기점 이후의 thread는 조사하지 않고 리턴하면 된다. 물론, sleep_list는 여전히 thread.c에서만 참조할 수 있기에, 보조 함수를 하나 생성해 호출하는 방식으로 sleep_list를 참조한다.
/* Timer interrupt handler. */
static void
timer_interrupt (struct intr_frame *args UNUSED) {
ticks++;
thread_wakeup (ticks);
thread_tick ();
}
이제 thread_wakeup을 구현한다. sleep_list에서 한 개씩 맨 앞에 놓여있는 thread의 wakeup_tick을 보며, 깨워야 할 thread라면, sleep_list에서 먼저 제거한 뒤, thread_unblock을 호출하여 깨워준다. 이대로 구현한 것이 아래 코드이다.
void
thread_wakeup (int64_t cur_tick) {
struct list_elem *e;
struct thread *t;
while (!list_empty (&sleep_list))
{
e = list_front (&sleep_list);
t = list_entry (e, struct thread, elem);
if (cur_tick < t->wakeup_tick)
break;
list_pop_front(&sleep_list);
thread_unblock(t);
}
}
이제 다 된 것 같으니 컴파일 후 실행을 해 보자.
4. 디버깅
우선 여기까지 구현했을 때, 제대로 동작해야 할 테스트 케이스는 아래와 같다.
- test_alarm_single
- test_alarm_multiple
- test_alarm_simultaneous
- test_alarm_zero
- test_alarm_negative
제목이 직관적이라 뭘 하는지는 코드를 보면 쉽게 이해할 수 있으므로 구체적인 설명은 생략하고, 개별 코드 실행을 어떻게 해야 하는지만 짚고 넘어가자. 우선 make를 한 뒤 build 바깥에서 pintos를 호출하면
os.dsk cannot be temporal.
을 뱉으며 그냥 죽어버린다. utils/pintos 파일을 보면 os.dsk 경로를 못 찾아서 생기는 현상이므로, os.dsk가 존재하는 build에서 실행해주면 문제가 없을 것이다. 구체적인 개별 실행 명령은 make check를 했을 때 나오는 명령어를 참조하였다.
pintos -v -k -T 60 -m 20 -- -q run alarm-single
pintos -v -k -T 60 -m 20 -- -q run alarm-multiple
pintos -v -k -T 60 -m 20 -- -q run alarm-simultaneous
pintos -v -k -T 60 -m 20 -- -q run alarm-zero
pintos -v -k -T 60 -m 20 -- -q run alarm-negative
이제 개별적으로 실행을 한 뒤, idle 시간이 0이 아니면 성공한 것이다. busy wait이 발생할 경우, 실행된 tick이 모두 kernel tick으로 집계되기 때문에 기존 Pintos 구현에서는 다음과 같은 결과가 나온다.
$ pintos -v -k -T 60 -m 20 -- -q run alarm-single
...
Execution of 'alarm-single' complete.
Timer: 294 ticks
Thread: 0 idle ticks, 294 kernel ticks, 0 user ticks
...
그리고 수정 결과 이렇게 idle tick이 늘어난 것을 확인할 수 있다.
$ pintos -v -k -T 60 -m 20 -- -q run alarm-single
...
Execution of 'alarm-single' complete.
Timer: 294 ticks
Thread: 250 idle ticks, 44 kernel ticks, 0 user ticks
...
make check를 잠시 진행했을 때, 나머지 테스트 케이스에서도 통과하는 것을 보아, 제대로 구현이 된 것을 알 수 있다.
5. 후기
Pintos 첫 코딩을 마치며 소감은 이런 식으로 일지를 남기는 것이 정말 어렵다는 것이다. 내 생각을 정리된 글로 풀어쓴다는 것이 쉬운 일이 아니고, 특히 이걸 더 복잡한 앞으로의 과제에서 해야 한다는 것이 내 의욕을 갉아먹는다. 그래도... 해야지 뭘...
'코딩 삽질 > KAIST PINTOS (CS330)' 카테고리의 다른 글
| (2021.10.12) Priority Scheduling - preemption (0) | 2021.10.12 |
|---|---|
| (2021.10.06) Priority Scheduling 가이드라인 (0) | 2021.10.06 |
| (2021.10.02) Alarm Clock - timer_sleep() 수정 (0) | 2021.10.01 |
| (2021.10.01) Alarm Clock 가이드라인 (2) | 2021.10.01 |
| (2021.09.06) 핀토스 셋업 삽질 (0) | 2021.09.06 |