1. 1주차 요약 정리
드디어 첫 주가 끝났다..첫 주차를 마치고서 정리하는 시간을 가져보자.
이 부분은 링크로 대신한다.
1. 시작 전 개념 정리
2. 과제 start
- 1.1 Alarm Clock
- 1.2 Priority Scheduling
- 1.2 (2) Priority Scheduling - Synchronization
- 1.2 (3) Priority Schedulig - Donation
2. Q&A 세션 정리
Q. void thread_yield(void) 함수에서 do_schedule() 함수를 호출하기 전에 intr_disable로 인터럽트를 off하는 이유
A.
Interrupt를 disable하는 이유는 CPU의 제어권을 interrput가 다시 enable될 때까지 넘겨주지 않겠다는 것을 의미한다. interrupt는 스케쥴링에 중요한 time interrupt, 혹은 입출력 장치에 의해 발생하는 I/O interrupt 등이 포함될 수 있다. 위의 do_schedule 함수는 CPU 제어권을 넘겨주는 함수이다. 이 과정에서 인터럽트로 인해 CPU 제어권이 엉뚱한 곳으로 넘어가면 synchronization(동시에 두 스레드/프로세스가 하나의 공유 자원에 접근하는 것을 막는)을 확보할 수 없게 된다.
그렇다고 interrupt disble을 남발하게 되면 OS의 response time을 증가시키기 때문에(그만큼 인터럽트 요청이 바로 이뤄지지 않으니 사용자는 기다려야 하니까) 좋지 않은 디자인으로 평가받는다. 그렇기 때문에 interrupt disable 대신 semaphore 혹은 lock과 같이 synchronization primitive(동기화 단위)를 주로 사용한다. 하지만 현재 과제에서는 scheduling에 직접적으로 연관되는 함수를 쓰기 때문에 interrupt disable로 sync를 확보한다. 혹은 lock/semaphore를 사용할 수 없을 때(ex - lock/semaphore 자체를 구현할 때) 역시 intr_disable을 사용한다.
만약 interrput enable/disable이 없다고 할 때 발생하는 버그 케이스를 생각해보자.
- 현재 스레드 A가 thread_yield() 함수를 통해 다른 thread B에게 CPU 제어권을 넘기려고 시도한다.
- 현재 스레드 A는 ready_list에 삽입되어 다음 스케쥴링을 대기한다. 이때 ready_list에 스레드를 삽입하기 위한 함수 list_push_back()은 내부에 여러 instrcution으로 구성되어 있다. 따라서 내부 코드를 한 줄 한 줄씩 실행하고 있을 것이다.
- 이때 timer interrupt가 발생한다.
- 스케쥴링을 위해 intr_handler -> timer_interrupt -> thread_tick 함수가 호출되고, 스케쥴러는 현재 thread A(아직 thread_yield가 끝나지 않았음)가 yield가 필요하다고 판단해 intr_yield_on_return 함수를 통해 interrupt handler 종료와 함께 thread를 yield하려 시도한다.
- 여기서 다시 thread_yield()가 호출된다. 문제는 위의 2를 보면 아직 thread_yield()가 끝나지 않은 상황에서 다시 호출됐다는 점이다. 가장 첫번째 문제는 현재 스레드가 ready_list에 두 번 삽입되어 logical bug를 일으킬 수도 있고, 최악에는 thread A가 Ready_list에 완전히 삽입되지 않은 상태에서(즉, list의 fd/bk가 완전히 연결되지 않은 상황에서) 다시 ready_list에 접근해 invaild memory access에 의한 kernel panic 등이 발생할 수 있다.
또한, 우리가 뒤에서 배웠던 lock/semaphore 같은 sync primitive는 thread/process 간 race condition을 방지하기 위해 사용될 수 있지만 thread/interrupt handler 사이의 race condition을 방지할 수는 없다. 이 경우에는 interrupt disable을 사용해야 synchronization을 확보할 수 있다.
3. 무엇이 어려웠나
0. 막연함
대체 뭐부터 해야 하나.. 나같은 경우는 초반에 개념 익히기에 주력했다. 중후반에 가서 코드에 집중하기 시작했는데, 사람마다 시작하는 방식이 달라 무엇이 맞을지 혼동이 왔지만, 결국
1. 코드 흐름 파악
워낙 방대한 양이다보니 여기 갔다 저기 갔다..
2. 공부할 양이 방대 - 흐름잡기
개념 먼저? Gitbook 먼저? 코드 먼저? 무엇부터 시작하는 게 좋은지가 명확하지 않아 서로가 각자 방식대로 진행. 막판에 코드 맞추는 식으로.
4. 더 생각해볼 점
- 이번주: 어떻게 CPU가 제어권을 넘겨받고 하는지를 배웠다. timer clock는 CPU 제어권과 관련!
- 우리가 프로그래머가 되면 해야 할 일: 이미 진행 중인 코드에서 수정해야 하는 일이! 스켈레톤은 만들어져 있고 지엽적인 문제를 풀 텐데. 당장은 OS 전체가 어떻게 구성되어 있는지 다 알 길이 없으나 프로젝트가 진행됨에 따라 점점 범위가 확장될 것!
- 각 구조체별로 실제로 자료구조가 어떻게 구현이 어떻게 되어 있는지? ex) 세마포어: 링크드 리스트로 구현. 다른 구조체는?
- sort는 왜 필요하며 언제 써야 하고 왜 최대한 쓰지 않는 방향으로 해야하나 -> O(NlogN)! sort를 빼야 할 경우는 언제?
- 원자성을 위반한 오류 - sema up/down은 반드시 겹치지 않고 분리되어야!
- 커널 패닉 - backtrace
- GDB 디버거 사용법! (이건 따로 정리 꼭 해두자) - 디버깅으로 printf로도 가능!
- signal: 인터럽트를 시스템적으로 추상화
- APIC: 인터럽트를 하드웨어로 구현.