크래프톤 정글 TIL : 0901

lazyArtisan·2024년 9월 1일
0

정글 TIL

목록 보기
63/147

🖥️ PintOS


슬랙 질문

오늘 하루 종일 붙잡고 있던 문제라 근본적인 원인이 너무 궁금해서 여쭤보고 싶습니다.
(사진 이름 2와 2_2는 오류 상황, 1은 해결된 상황에 대한 스크린샷입니다.)
전체 코드 : https://github.com/lazyArtisan/pintos-kaist/tree/priority-donation

  1. 발단 : single-level feedback queue 구현해서 다른 건 다 통과하는데 test case 중 mlfqs-recent-1만 fail

  2. 증상
    a. 테스트를 시작하면 10초 뒤에 테스트 파일 문구가 무한반복됨.
    b. 그 후 일정 시간이 지나면 테스트 결과가 2초에 한 번씩 출력되는데, 일정 패턴을 보이며 recent_cpu가 출력됨.
    1. recent_cpu의 10의 자리부터 소수점 자리까지는 일정함. 이는 10초에 한 번씩 바뀜.
    2. 1 tick씩 증가할 때마다 recent_cpu가 1씩 증가하는 것 외에 recent_cpu 식의 영향이 가해지지 않음. 10초에 한 번씩 크게 변동이 있긴 함.

  3. 해결 방법 : 1초, 그리고 4틱마다 모든 쓰레드를 순회하기 위해 관리하던 all_thread_list에 쓰레드를 넣어주는 구문을 thread_create에서 init_thread로 위치를 변경해줌.

  4. 의문 : 애초에 thread_create가 init_thread를 호출하고 있고, 정답과 오답 위치 사이에는 별다른 시스템 콜도 없이 평범하게 초기화가 되는 구문들 밖에 없는데
    왜 thread_create에서 all_thread_list에 현재 만들어지고 있는 쓰레드를 넣으면 작동을 안하고,
    init_thread에서 all_thread_list에 넣으면 작동을 하는 건지?

  5. 의문에 대한 (부족한) 답 : thread_init을 할 때 main 쓰레드는 thread_create가 아닌 init_thread만을 호출함. main 쓰레드가 all_thread_list에 들어가지 못한 것이 문제를 일으켰을 것으로 추정.

  6. 근본적인 의문
    a. main 쓰레드가 all_thread_list에 들어가지 못한 것이 ready_threads의 value에 영향을 끼쳐 recent_cpu의 계산에 문제를 일으켰다면, 도대체 어떻게 영향을 미친건지, 그리고 왜 다른 테스트 케이스들은 멀쩡하게 통과한건지?
    b. main 쓰레드는 왜 init_thread만 호출하고 마는 건지?
    c. thread_init이 정확히 무엇을 해주는 것인지? 주석을 읽어보았지만 정확한 원리가 쓰여있지 않음. 구체적으로 이 부분들의 좀 더 fundamental한 설명이 궁금함.

  7. Initializes the threading system by transforming the code that's currently running into a thread.

  8. loader.S was careful to put the bottom of the stack at a page boundary.

그냥 주말도 아니고 일요일인데 여쭤봐서 죄송합니다.
코드가 박살난 걸 고쳐야 하는 것 같이 시급한 문제는 아니니 시간 나실 때 여유롭게 답변해주세요.
답변해주셔서 항상 감사합니다.

TA님 답변

a. main 쓰레드가 all_thread_list에 들어가지 않았기에 timer.c의 L196 에서 main 쓰레드에 대한 update_recent_cpu가 동작하지 않았던 것 같습니다. 따라서 main 쓰레드의 recent_cpu는 L186에 의해 계속 증가하고만 있던 것이 아닐까요? 전 이게 문제였다고 생각합니다.

b. thread_create는 이미 실행되고 있는 쓰레드가 새로운 쓰레드를 만들때 호출됩니다. 따라서 새로운 thread구조체를 만드는 것이 필요하고, 새 쓰레드의 스택 영역 등 메모리 할당을 L235 에서 한 후 init_thread를 통해 몇몇 초기화를 진행합니다. 하지만 main쓰레드는 이미 운영체제가 시작될때 돌고있는 쓰레드라서 이미 자기가 사용중인 스택영역이 있고, 그걸 그대로 thread 구조체를 위한 메모리로 사용하므로 새로운 메모리를 할당할 필요가 없습니다. 따라서 바로 init_thread를 호출하여 초기화를 진행하게 됩니다.

c. thread_init에서 / set up a thread structure ... / 부분 이전까지는 그냥 다양한 변수 초기화를 진행하고, 그 아래에선 main 쓰레드가 정상적으로 threading system에 의해 스케쥴링 될 수 있도록 초기화를 진행합니다. 구체적으로 main 쓰레드에 대해 아직 struct thread * 구조체가 없는 상황인데 이걸 똑바로 만드는 작업을 한다고 생각하면 됩니다.

자세한 내용은 https://casys-kaist.github.io/pintos-kaist/appendix/threads.html 여기의 설명을 곁들여서 이해하시면 될 것 같습니다. 아래 첨부해놓은 그림대로 각각의 쓰레드는 struct thread 를 포함한 메모리 영역(1개의 page)을 할당받게 되고, 그 페이지에서 아랫쪽 끝부분에는 struct thread의 내용이 있고, 위쪽 끝부분부터는 스택 영역으로 사용됩니다. 이러한 공통적인 구조는 threading system을 위해 유지되어야 하므로 main 쓰레드도 똑같이 만들어줘야 합니다.

하지만 main 쓰레드의 경우 thread_init이 호출되기 전에는 struct thread 없이 스택 영역만 가지고 실행하게 됩니다. 하지만 "loader.S was careful to put the bottom of the stack at a page boundary"라고 적혀있듯이 main 쓰레드의 스택 영역은 운좋게도 page boundary의 위쪽 꼭대기에 알맞게 위치해 있어서 struct thread 구조체를 위한 새로운 메모리를 할당하지 않고 그 page의 내부에서 struct thread*를 초기화하면 되게 됩니다. 그 과정이 모두 끝나면 main쓰레드도 다른 쓰레드와 똑같은 형태를 갖게 되어 threading system이 정상 동작할 수 있게 됩니다.

0개의 댓글