OS/PintOS

[Pintos] project1: Threads 플로우 차트

:) :) 2024. 9. 3. 19:02

 

 

Introduction · GitBook

No results matching ""

casys-kaist.github.io

 

 

GitHub - casys-kaist/pintos-kaist

Contribute to casys-kaist/pintos-kaist development by creating an account on GitHub.

github.com

 

Thread Life cycle

main 함수에서 thread_init () 함수를 실행하면

default thread 인 main thread가 하나 생성됩니다.

 

이후

thread_start () 함수를 통해 스레드 스케쥴러가 작동하기 시작합니다.

따라서 이 함수를 통해 스레드의 라이프사이클의 시작을 확인해볼 수 있습니다.

 

thread_start 함수는 다음과 같은 역할을 합니다.

/* Starts preemptive thread scheduling by enabling interrupts.
   Also creates the idle thread. */

인터럽트를 받을 수 있도록 하여 선점가능한 스레드 스레쥴링을 시작합니다.

또한 idle thread, 즉 현재 cpu를 점유할 스레드가 없을 때 작동하는 유휴 스레드를 생성해줍니다.

 

먼저 유휴 스레드를 생성합니다.

세마포어 설정

유휴 스레드를 생성하는 중에는 idle thread에 대한 세마포어를 0으로 초기화 해

idle thread의 초기화 작업에 문제가 생기지 않도록

이후 idle 함수에서 sema_up(&idle_started) 하기 전까지 대기하도록 합니다.

 

 

이후 인터럽트를 받을 수 있게 설정합니다.

Pintos의 인터럽트는 아래 내용을 참고하면 좋을 것 같습니다.

인터럽트는 cpu 단위로 각각 다른 상태를 유지하는데,

pintos는 단일 cpu를 가정한, 단일 cpu 멀티 스레딩 환경이어서

시스템 전체에서 유지하는 인터럽트 상태는 하나입니다. (interrupt ON or OFF)

[링크]

threads/interrupt.c 파일 속에 x86_64 아키텍처의 인터럽트 개수가 명시되어있습니다.

256개라 합니다.

 

intr_enable () 함수는 인터럽트를 활성화하며,

활성화 이전 인터럽트의 상태를 반환합니다.

sti는 인터럽트 플래그를 설정함으로써 인터럽트를 활성화하는 어셈블리 명령어입니다.

 

intr_disable () 함수는 인터럽트를 비활성화하며,

비활성화 이전 인터럽트의 상태를 반환합니다.

cli는 인터럽트 플래그를 제거함으로써 인터럽트를 비활성화하는 어셈블리 명령어입니다.

 

인터럽트 상태, 즉 인터럽트 level은 interrupt.h 헤더에 존재합니다.

활성화, 비 활성화 두 가지의 상태만 존재

 

 

 

 

(포스팅 분할 요망)


 

 

idle thread의 초기화를 sema_down 함수에서 기다리게 됩니다.

 

 

 

 


ㅠㅠ 컴퓨터 구조에 대한 이해가 부족해 thread가 어떻게 작동하는 지 한참 고민하면서 찾았습니다ㅠㅠ

thread의 상태는 timer가 1번씩 tick 할 때마다 결정되게 됩니다.

timer_tick을 단위로 해서 block 하거나, 다시 ready 상태로 가져오는 등의 작업을 진행할 수 있습니다.

다시 말 해, thread의 작업 단위는 timer의 1 tick이 됩니다.

 

흠 근데 그러면 timer의 tick을 1번씩 증가시켜주는 건 누가 하는 일인가요?

저는 처음에 여기서 너무 헷갈렸고, 고민을 많이 했습니다.

 

처음에는 timer의 tick 증가를 OS가 하는 줄 알았습니다.

timer가 하드웨어일 거라는 생각을 못했고, timer에서 interrupt가 올 때마다 timer_tick이 1 씩 증가하는 구조를 가지는 걸 확인했는데요.

 

그래서 전부 다 OS가 관리하는 소프트웨어로 구현되어있을 줄 알았고, 소스코드단에서 찾으려고 하니 찾을 수가 없었습니다ㅠㅠ.

 

PintOS가 사용하는 timer는 PIT(Programmable Interval Timer)로써, 하드웨어 기반 타이머입니다.

timer_init 함수에서 timer의 tick 주기를 설정할 수 있고, 이를 참고해 timer_init 함수는 PIT와 연결된? 0x40 포트에 주기 값을 설정합니다.

또한 타이머는 하드웨어이므로 OS 외부에서 발생하는 인터럽트이고,

이를 핸들링 하기 위해 intr_register_ext 함수를 통해 "8254 Timer"로 명명한 인터럽트를 핸들러와 함께 0x20번 (10진수로는 32번) 인터럽트로 등록합니다.

 

intr_register_ext 함수, 0x20 번 부터 0x2f 까지 총 16개만 등록 가능하다.

 

 

이후 PIT 타이머가, 설정한 주기마다 0x20번 외부 인터럽트 "8254 Timer"를 주게 되면,

"8254 Timer"인터럽트에 등록된 timer_interrupt () 라는 핸들러를 작동시키게 됩니다.

이 때 PintOS 전체에서 바라보는 timer_tick이 1 증가하게 됩니다.

ticks++; 을 통해 tick을 1 증가시킨다

 

이후 thread_tick () 함수를 통해

tick의 1 증가로 인한 변화를 스레드에 반영합니다.

 

우선 시스템 전체의 통계량(아마 모니터링을 위해 유지할)에 변화를 반영합니다.

(idle_ticks++ 혹은 user_ticks++, kernel_ticks++)

 

이후 현재 스레드의 tick을 1 증가시키고, 그게 TIME_SLICE(default value = 4) 보다 커지면

intr_yield_on_return () 함수를 통해 스케쥴링을 강제합니다(선점 스케쥴링).

intr_yield_on_return

yield_on_return

이 boolean 변수는 인터럽트가 반환될 때 현재 스레드가 다른 스레드로 교체되어야 하는지를 나타냅니다.

 

다시 intr_handler 함수를 찾아보면,

모든 인터럽트 핸들러는 yield_on_return 이라는 조건을 처리하는 구문이 존재합니다.

timer interrupt는 외부 인터럽트 이며, 이런 외부 인터럽트에 대해

yield_on_return 필드가 true이면

thread_yield 함수를 호출해, 현재 스레드를 스케쥴링 대상으로 삼아 스케쥴링을 진행합니다.

(Pintos 는 single thread 프로그램이었나??)

 

 

 

 

 

 

thread.c의 파일 최상단에서, thread_ticks 변수 정의를 찾아보면

값 할당 혹은 값 초기화 과정이 없어서 이런 의문이 들었습니다.

현재 수행중인 thread에 대한 tick은 어디서 유지하고 있을까요?

 

(변수의 선언과 정의도 구분해서 사용하더라고요, 링크입니다.)

 

 

아래 schedule 함수에서 thread_ticks를 0으로 초기화합니다.

 

 

schedule 함수는 do_schedule 이라는 함수에서 호출됩니다.

 

 

그렇다면 do_schedule 함수는요?
스레드 스레쥴링을 하는 함수니까 뭔가 스레드가 CPU를 사용하고 나올 때와 관련이 있을 것 같습니다.

 

네, 위처럼 스레드가 CPU를 사용하고 물러선 이후의 내용을 담은 thread_yield () 함수에서 do_schdule 함수를 호출하네요.

(running state를 가지는 스레드를 ready state(cpu 사용할 수 있지만 대기중인 상태)로 바꾸는게 yield 과정입니다.)

 


음 근데 결과적으로 thread_ticks 변수를 처음 초기화 하는 건 thread_start() 함수를 통해 처음 idle 스레드를 만들 때 실행합니다.

idle 스레드는 무한히 현재 자기 자신을 block 하고 다시 스케쥴링받아오는 과정을 반복합니다.

 

thread_block () 과정은

현재 스레드를 블락시킨 후에

다음에 스케쥴링할 스레드를 찾고 스케쥴링을 진행하게 되는데 (스레드 변경)

이 때 schdule () 함수를 호출하여

현재 스레드가 실행된 tick을 유지할 변수 thread_ticks를 0으로 초기화 합니다!

 

 

 

 

 

 

 

좋습니다....!

한 번 스레드 라이프사이클에 대한 플로우 차트를 그려볼 수 있을 것 같아요

thread, interrupt, thread_ticks 간의 관계로 표현해볼 수 있을 것 같습니다!

나름대로 나눠보았다.

나름대로 여러 요소로 나눠 이해한 흐름을 그려보려고 했으나...

흠... OS의 작동방식을 2차원 평면에 나타내려고 하는 게 잘못되었던 걸까요...?

어떻게 더 잘 표현할 수 있을 것 같은데 마땅한 방법이 떠오르질 않네요..

 

일단 PlantUML으로 다시 그려보도록 하겠습니다.

 

힘드네요...

 

정말 어렵습니다...

해설은 내일 적겠습니다...

'OS > PintOS' 카테고리의 다른 글

[Pintos] Priority Scheduling  (1) 2024.09.05
[Pintos] Alarm Clock  (1) 2024.09.05
PintOS 프로젝트 - 프로그램 이해를 위한 플로우 차트 그리기  (1) 2024.09.03