IT/์ปดํ“จํŒ…์  ์‚ฌ๊ณ 

Pintos Project 1-1. Busy Waiting vs. Sleep/Wakeup

Unused 2025. 5. 12. 15:46

์ธํŠธ๋กœ

ํฌ๋ž˜ํ”„ํ†ค ์ •๊ธ€ 9์ฃผ์ฐจ, Pintos ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉฐ ๋ฐฐ์šฐ๊ณ  ๋А๋‚€ ์ ๋“ค์„ ๊ณต์œ ํ•˜๋Š” ์‹œ๋ฆฌ์ฆˆ์˜ ์ฒซ ๋ฒˆ์งธ ๊ธ€์ž…๋‹ˆ๋‹ค. Pintos ํ”„๋กœ์ ํŠธ๋Š” ์ด๋ก ์œผ๋กœ๋งŒ ๋ฐฐ์šฐ๋˜ ์šด์˜์ฒด์ œ์˜ ํ•ต์‹ฌ ๊ฐœ๋…๋“ค์„ ์ง์ ‘ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•ด๋ณด๋ฉด์„œ ๊นŠ์ด ์ดํ•ดํ•  ์•„์ฃผ ์ข‹์€ ๊ธฐํšŒ์ธ๋ฐ์š”. ๊ทธ ์ฒซ ๋ฒˆ์งธ ํŒŒํŠธ์ธ '์•Œ๋žŒ ์‹œ๊ณ„(Alarm Clock)' ๊ตฌํ˜„์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด ๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ : ์ €ํฌ๋Š” ์Šคํƒ ํฌ๋“œ๋Œ€ CS 112/212์˜ ์˜ค๋ฆฌ์ง€๋„ Pintos๊ฐ€ ์•„๋‹Œ, x86-64์— ๋งž์ถ˜ Pintos-KAIST๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ํฌ์ŠคํŠธ ๋˜ํ•œ ์ด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค.


๊ณผ์ œ ๋ชฉํ‘œ: timer_sleep() ๊ฐœ์„ 

Credit: https://slideplayer.com/slide/13132371/

์ €ํฌ๊ฐ€ ๋‹ค๋ฃฐ ํ•จ์ˆ˜๋Š” devices/timer.c์— ์ •์˜๋œ void timer_sleep(int64_t ticks)์ž…๋‹ˆ๋‹ค. ์ตœ์ดˆ๋กœ ๋ฐ›์•˜์„ ๋‹น์‹œ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋Š” ์œ„์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜์˜ ์—ญํ• ์€ ํ˜„์žฌ ์Šค๋ ˆ๋“œ์˜ ์‹คํ–‰์„ ticks๋งŒํผ์˜ ํƒ€์ด๋จธ ํ‹ฑ(tick) ์‹œ๊ฐ„ ๋™์•ˆ ์ค‘๋‹จ์‹œํ‚ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ปค์„œ๋ฅผ ๊นœ๋นก์ด๊ฑฐ๋‚˜ ์ผ์ • ์‹œ๊ฐ„ ๋Œ€๊ธฐํ•ด์•ผ ํ•˜๋Š” ์ž‘์—…์— ์œ ์šฉํ•˜์ง€์š”. ๋ฌธ์ œ๋Š” timer_sleep()์˜ ๊ตฌํ˜„ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ด์ฃผ์„ธ์š”.

/* Suspends execution for approximately TICKS timer ticks. */
void timer_sleep (int64_t ticks) {
	int64_t start = timer_ticks ();

	ASSERT (intr_get_level () == INTR_ON);
	while (timer_elapsed (start) < ticks)
		thread_yield ();
}

์ด๊ฒƒ์ด ๋ฐ”๋กœ Busy Waiting (๋ฐ”์œ ๋Œ€๊ธฐ) ๋˜๋Š” Spin Waiting (์Šคํ•€ ๋Œ€๊ธฐ)๋ผ๋Š” ๋น„ํšจ์œจ์ ์ธ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์Šค๋ ˆ๋“œ๋Š” ์ž์‹ ์ด ๊นจ์–ด๋‚˜์•ผ ํ•  ์‹œ๊ฐ„(start+ticks)์ด ๋  ๋•Œ๊นŒ์ง€ while ๋ฃจํ”„๋ฅผ ๊ณ„์† ๋Œ๋ฉด์„œ ํ˜„์žฌ ์‹œ๊ฐ„์„ ํ™•์ธํ•˜๊ณ , ์‹œ๊ฐ„์ด ๋˜์ง€ ์•Š์•˜์œผ๋ฉด thread_yield()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ CPU๋ฅผ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—๊ฒŒ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค. ๋„˜๊ฒจ์ฃผ๊ธฐ๋Š” ํ•˜์ง€๋งŒ, while๋ฌธ์„ ๋Œ๋ฉด์„œ ์ €๋Ÿฌ๋‹ˆ, ๋งˆ์น˜ ์–ด๋– ํ•œ ์‚ฌ๋žŒ์„ 5๋ถ„๊ฐ„ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ํ•  ๋•Œ, ๊ทธ๋ƒฅ ๊ฐ€๋งŒํžˆ ์žˆ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๊ณ„์† ์‹œ๊ณ„๋ฅผ ์ณ๋‹ค๋ณด๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. CPU๋Š” ์ž๊พธ ์‹œ๊ณ„๋ฅผ ์ณ๋‹ค๋ด์•ผ ํ•˜๋‹ˆ ๋‹ค๋ฅธ ์ผ์„ ํ•  ์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.


ํ•ด๊ฒฐ์ฑ…: ์Šค๋ ˆ๋“œ๋ฅผ ์žฌ์›Œ๋‘๊ณ , ํƒ€์ด๋จธ ์ธํ„ฐ๋ŸฝํŠธ๋กœ ๊นจ์šฐ๊ธฐ

Credit: https://woonys.tistory.com/142

Busy Waiting์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•œ ํ•ต์‹ฌ ์•„์ด๋””์–ด๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ๋ธ”๋ก(Block) ์ƒํƒœ๋กœ ๋งŒ๋“ค์–ด์„œ ์Šค์ผ€์ค„๋ง ๋Œ€์ƒ์—์„œ ์ œ์™ธ์‹œํ‚ค๊ณ , ์ •ํ•ด์ง„ ์‹œ๊ฐ„์ด ๋˜์—ˆ์„ ๋•Œ ๋ˆ„๊ตฐ๊ฐ€ ์ด ์Šค๋ ˆ๋“œ๋ฅผ ๊นจ์›Œ์„œ(Unblock) ๋‹ค์‹œ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Pintos ํ™˜๊ฒฝ์—์„œ ์ด ์—ญํ• ์„ ๋‹ด๋‹นํ•˜๊ธฐ์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๊ฒƒ์€ ๋ฐ”๋กœ ํƒ€์ด๋จธ ์ธํ„ฐ๋ŸฝํŠธ ํ•ธ๋“ค๋Ÿฌ์ž…๋‹ˆ๋‹ค. ํƒ€์ด๋จธ ์ธํ„ฐ๋ŸฝํŠธ๋Š” ์ผ์ •ํ•œ ๊ฐ„๊ฒฉ(TIMER_FREQ, ๊ธฐ๋ณธ๊ฐ’ 100Hz)์œผ๋กœ ๋ฐœ์ƒํ•˜๋ฉฐ, ์ด๋•Œ๋งˆ๋‹ค ํ˜„์žฌ ์‹œ๊ฐ„์ด 1ํ‹ฑ์”ฉ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ธํ„ฐ๋ŸฝํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค ์ž ๋“ค์–ด ์žˆ๋Š” ์Šค๋ ˆ๋“œ๋“ค ์ค‘ ๊นจ์–ด๋‚  ์‹œ๊ฐ„์ด ๋œ ์Šค๋ ˆ๋“œ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ๊นจ์›Œ์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

ํƒ€์ด๋จธ ์ธํ„ฐ๋ŸฝํŠธ๋ž€?

ํƒ€์ด๋จธ ์ธํ„ฐ๋ŸฝํŠธ๋Š” OS์—์„œ ์‹œ๊ฐ„์„ ๊ด€๋ฆฌํ•˜๊ณ  ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ ํ•„์ˆ˜์ธ ๊ตฌ์„ฑ์š”์†Œ์ž…๋‹ˆ๋‹ค. ์ฃผ๋กœ ํ•˜๋“œ์›จ์–ด ํƒ€์ด๋จธ ์นฉ(๋˜๋Š” CPU ๋‚ด์žฅ ํƒ€์ด๋จธ)์ด ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ฐœ์ƒ์‹œํ‚ค๋ฉฐ, OS์˜ "์‹ฌ์žฅ ๋ฐ•๋™" ๊ฐ™์€ ์—ญํ• ์ž…๋‹ˆ๋‹ค. ์ด ์ธํ„ฐ๋ŸฝํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค OS๋Š” ๋ฏธ๋ฆฌ ๋“ฑ๋ก๋œ ์ธํ„ฐ๋ŸฝํŠธ ํ•ธ๋“ค๋Ÿฌ(ISR, Interrupt Service Routine)๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋‹ค์–‘ํ•œ ์‹œ๊ฐ„ ๊ด€๋ จ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒˆ์— ์ €ํฌ๊ฐ€ ๊ตฌํ˜„ํ•˜๋Š” alarm clock ๊ฐ™์€ ๊ฒƒ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.

์‹ค์ œ ์‚ฌ๋ก€

ํ˜„๋Œ€ OS์—์„œ ํƒ€์ด๋จธ ์ธํ„ฐ๋ŸฝํŠธ๋Š” ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ธํ„ฐ๋ŸฝํŠธ ๋ฐœ์ƒ ์ฃผ๊ธฐ๋Š” ์„ค์ •์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€์ง€๋งŒ ์ „ํ†ต์ ์œผ๋กœ "Jiffy" ๋˜๋Š” "Tick" ์ด๋ผ๋Š” ์‹œ๊ฐ„ ๋‹จ์œ„๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋“ค๋„ ๊ธฐ๋ณธ์ ์œผ๋กœ Pintos์™€ ๋™์ผํ•˜๊ฒŒ 100 Hz, ์ฆ‰, 10 ms๋‹น 1์”ฉ ์˜ค๋ฅด๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. ํ•œํŽธ, Windows 9x ๊ณ„์—ด (95, 98, Me ๋“ฑ) ์—ญ์‹œ ์ด๋Ÿฐ ๊ธฐ๋Šฅ์ด ์žˆ์—ˆ๋Š”๋ฐ, ์ด ๊ณผ์ •์—์„œ 32๋น„ํŠธ ์นด์šดํ„ฐ ์˜ค๋ฒ„ํ”Œ๋กœ๋กœ ์ธํ•œ ์œ ๋ช…ํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์œผ๋‹ˆ, ๋ฐ”๋กœ ๊ทธ 49.7์ผ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

Windows 9x๋Š” ๋‹น์‹œ ๊ฐ€์ •์šฉ PC์— ์ตœ์ ํ™”๋œ ๊ฐ€๋ฒผ์šด ๊ตฌ์กฐ์™€ ํ•˜์œ„ํ˜ธํ™˜์œผ๋กœ ๋งŽ์€ ์‚ฌ๋ž‘์„ ๋ฐ›์•˜์œผ๋‚˜ ๋™์‹œ์— ํ—ˆ์ˆ ํ•œ ๊ด€๋ฆฌ/๋ณดํ˜ธ, ๋ถˆ์•ˆ์ •์„ฑ, ์จ๋“œํŒŒํ‹ฐ ๋“œ๋ผ์ด๋ฒ„๋“ค์˜ ๋ถ€์กฐํ™”๋กœ ์•…๋ช…์ด ๋†’์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์š”์ฆ˜์˜ PC์•ผ ํ•œ๋ฒˆ ์ผœ๋†“๊ณ  ๋ฉฐ์น  ๋ช‡๋‹ฌ์„ ์ผœ๋†“๋Š” ๊ฒŒ ์ผ์ƒ์ด๋ผ์ง€๋งŒ 9x๋Š” ํŠน์œ ์˜ ๋ถˆ์•ˆ์ •์„ฑ์œผ๋กœ ๊ณ ์ž‘ ์ˆ˜ ์‹œ๊ฐ„๋งŒ ์ผœ๋†“์•„๋„ ๋ธ”๋ฃจ ์Šคํฌ๋ฆฐ ๋•Œ๋ฌธ์— ์žฌ๋ถ€ํŒ…์ด ํ•„์ˆ˜์˜€์ง€์š”. ๊ทธ๋Ÿฌ๋‚˜ ์„ค๋ น ๋ฒ„ํ‹ด๋‹ค ์ณ๋„ ๊ฒฐ๊ตญ uptime์ด 49.7์ผ์€ ๋„˜๊ธธ ์ˆ˜ ์—†์—ˆ์œผ๋ฉฐ, ์ดํ›„ ๋ฌด์กฐ๊ฑด ์žฌ๋ถ€ํŒ…ํ•ด์•ผ๋งŒ ํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค. 

  • Windows 9x๋Š” ์‹œ์Šคํ…œ์ด ๋ถ€ํŒ…๋œ ์ดํ›„ ๊ฒฝ๊ณผ๋œ ์‹œ๊ฐ„์„ 1 ms ๋‹น 1์”ฉ ์˜ฌ๋ผ ๋ถ€ํ˜ธ ์—†๋Š”(unsigned) 32๋น„ํŠธ ์ •์ˆ˜์— ์ €์žฅํ•˜๊ณ , ์ด๋ฅผ GetTickCount()์™€ ๊ฐ™์€ API๋ฅผ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ œ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ถ€ํ˜ธ ์—†๋Š” 32๋น„ํŠธ ์ •์ˆ˜๊ฐ€ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ“๊ฐ’์€ 4,294,967,295์ž…๋‹ˆ๋‹ค. ์ด ์นด์šดํ„ฐ๊ฐ€ 1 ms๋‹น 1์”ฉ ์˜ค๋ฅด๋ฏ€๋กœ, ์นด์šดํ„ฐ๊ฐ€ ์ตœ๋Œ“๊ฐ’์— ๋„๋‹ฌํ•˜์—ฌ ๋‹ค์‹œ 0์œผ๋กœ ๋Œ์•„์˜ค๊ธฐ๊นŒ์ง€ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.
    • ์ตœ๋Œ€ ๋ฐ€๋ฆฌ์ดˆ ์ˆ˜:
    • 1์ผ = 24 * 60 * 60 * 1000 = 86,400,000 ms
    • ์นด์šดํ„ฐ๊ฐ€ ์˜ค๋ฒ„ํ”Œ๋กœํ•˜๊ธฐ๊นŒ์ง€ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„ (์ผ)

Credit: https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount64

์•„๋ฌดํŠผ, ํ•ด๊ฒฐ์ฑ…์„ ์ฐพ์•˜์œผ๋‹ˆ ์ด์ œ ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•  ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค. ์šฐ์„  Pintos์˜ ์Šค๋ ˆ๋“œ ๊ด€๋ จ ๋ถ€๋ถ„์ธ thread.h๋ถ€ํ„ฐ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

wakeup_tick ๋ณ€์ˆ˜ ์ถ”๊ฐ€

์•„๋ž˜์™€ ๊ฐ™์ด thread.h์˜ struct thread ์„ ์–ธ๋ถ€์—์„œ, int64_t wakeup_tick;๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ•ด๋‹น ์Šค๋ ˆ๋“œ์˜ ๊นจ์šธ ์‹œ๊ฐ์„ ์ €์žฅํ•˜๋Š” ๋ณ€์ˆ˜์ž…๋‹ˆ๋‹ค.

์Šฌ๋ฆฝ ๋ฆฌ์ŠคํŠธ ์ „์—ญ ๋ณ€์ˆ˜ ์ถ”๊ฐ€

๋‹ค์Œ์€ thread.c์ž…๋‹ˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด ๋‘ ๊ฐœ์˜ ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

  • static struct list sleep_list; ๊นจ์šธ ์ˆœ์„œ. insert ์‹œ, list_insert_ordered()๋กœ wakeup_tick ๊ธฐ์ค€ ์˜ค๋ฆ„์ฐจ์ˆœ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • static int64_t next_tick_to_awake; ์ตœ์ ํ™” ์š”์†Œ. ๋ชจ๋“  ์ž ๋“ค์–ด ์žˆ๋Š” ์Šค๋ ˆ๋“œ๋“ค ์ค‘ ๊ฐ€์žฅ ๋‹ค์Œ์— ๊นจ์›Œ์•ผ ํ•  ์Šค๋ ˆ๋“œ์˜ ํ‹ฑ์„ ๋‹ด๋Š” ๋ณ€์ˆ˜์ž…๋‹ˆ๋‹ค. ๊ฐ€๋ น, ์Šค๋ ˆ๋“œ A, B, C, D๊ฐ€ ์ž ๋“ค์–ด ์žˆ๊ณ , ๊ฐ๊ฐ ๋‹ค์Œ ๊นจ์›Œ์•ผ ํ•  ์‹œ์ ์ด 1200ํ‹ฑ, 1220ํ‹ฑ, 1180ํ‹ฑ, 1370ํ‹ฑ์ด๋ผ๋ฉด, next_tick_to_awake์—๋Š” 1180์ด๋ผ๋Š” ๊ฐ’์ด ๋“ค์–ด๊ฐ€ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด, ๋งค ์ธํ„ฐ๋ŸฝํŠธ๋งˆ๋‹ค ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์ด๋•Œ๋งŒ ์ž ๋“  ์Šค๋ ˆ๋“œ๋“ค์„ ํ™•์ธํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ•จ์ˆ˜ ์ถ”๊ฐ€

์•„๋ž˜์™€ ๊ฐ™์ด ํ•„์š”ํ•œ ํ•จ์ˆ˜๋“ค์„ thread.c์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

/**
 * thread_sleep: Alarm clock ๊ณผ์ œ. 
 *   Thread๋ฅผ sleep queue์— ์‚ฝ์ž…ํ•˜๊ณ  blocked ์ƒํƒœ๋กœ ๋งŒ๋“ค์–ด ๋Œ€๊ธฐ.
 * 
 * @param ticks: ํ•ด๋‹น ์Šค๋ ˆ๋“œ์˜ ํ‹ฑ
 */
void thread_sleep(int64_t ticks){
	struct thread* this;
	this = thread_current();

	if(this == idle_thread){
        ASSERT(0);
	}else{
		enum intr_level old_level = intr_disable();  // ์ธํ„ฐ๋ŸฝํŠธ ์ผ์‹œ์ค‘์ง€
		update_next_tick_to_awake(this->wakeup_tick = ticks); // Awake ticks ์—…๋ฐ์ดํŠธ
		list_push_back(&sleep_list, &this->elem); // sleep_list์— ๋„ฃ์Œ
		thread_block(); // ํ•ด๋‹น ์Šค๋ ˆ๋“œ๋ฅผ ๋ธ”๋ก
		intr_set_level(old_level); // ์ธํ„ฐ๋ŸฝํŠธ ์žฌ๊ฐœ
	}
}

/** 
 * thread_awake - Alarm Clock ๊ณผ์ œ. wakeup_tick๊ฐ’์ด ์ธ์ž๋กœ ๋ฐ›์€ ticks๋ณด๋‹ค ํฌ๊ฑฐ๋‚˜ ๊ฐ™์€ ์Šค๋ ˆ๋“œ๋ฅผ ๊นจ์›€.
 *   ํ˜„์žฌ ๋Œ€๊ธฐ ์ค‘์ธ ์Šค๋ ˆ๋“œ๋“ค์˜ wakeup_tick ์ค‘ ์ตœ์†Ÿ๊ฐ’์„ next_tick_to_awake ์ „์—ญ๋ณ€์ˆ˜์— ์ €์žฅ.
 */
void thread_awake(int64_t wakeup_tick){
	next_tick_to_awake = INT64_MAX;
	struct list_elem* sleeping = list_begin(&sleep_list);  // ์ž๊ณ  ์žˆ๋Š” ์Šค๋ ˆ๋“œ ์ „์ฒด๋ฅผ ๊ฐ€์ ธ์˜ด.

	while(sleeping != list_end(&sleep_list)){ // ๋ฆฌ์ŠคํŠธ ๋๊นŒ์ง€
		struct thread* thr = list_entry(sleeping, struct thread, elem);

		if(thr->wakeup_tick <= wakeup_tick){ // ๊นจ์šธ ๋•Œ๋ฉด
			sleeping = list_remove(&thr->elem); // ํ•ด๋‹น ์Šค๋ ˆ๋“œ๋ฅผ ์‚ญ์ œ
			thread_unblock(thr); // ํ•ด๋‹น ์Šค๋ ˆ๋“œ์˜ ๋ธ”๋ก ํ•ด์ œ
		}else{ // ์•„๋‹ˆ๋ฉด
			sleeping = list_next(sleeping); // ๋‹ค์Œ ์ž๋Š” ์Šค๋ ˆ๋“œ๋กœ ๋„˜์–ด๊ฐ
			update_next_tick_to_awake(thr->wakeup_tick); // wakeup_tick๋ฅผ ์—…๋ฐ์ดํŠธ
		}
	}
}

/** 
 * update_next_tick_to_awake - Alarm Clock ๊ณผ์ œ.
 *   ์ „์—ญ๋ณ€์ˆ˜์ธ next_tick_to_awake๋ฅผ, ticks์™€ ๋น„๊ตํ•˜์—ฌ, ๋‘˜ ์ค‘ ๋” ์ž‘์€ ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธ.
*/
void update_next_tick_to_awake(int64_t ticks) {
    next_tick_to_awake = (next_tick_to_awake > ticks) ? ticks : next_tick_to_awake; // ์ตœ์†Ÿ๊ฐ’ ๊ฐ€์ง„ ๋…€์„
}

/** 
 * get_next_tick_to_awake - Alarm Clock ๊ณผ์ œ.
 *   ์ „์—ญ๋ณ€์ˆ˜์ธ next_tick_to_awake๋ฅผ ๋ฆฌํ„ด.
*/
int64_t get_next_tick_to_awake(void) {
    return next_tick_to_awake;
}

ํ•จ์ˆ˜ ์ˆ˜์ •

void thread_init(void)๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

void
thread_init (void) {
	ASSERT (intr_get_level () == INTR_OFF);

	/* Reload the temporal gdt for the kernel
	 * This gdt does not include the user context.
	 * The kernel will rebuild the gdt with user context, in gdt_init (). */
	struct desc_ptr gdt_ds = {
		.size = sizeof (gdt) - 1,
		.address = (uint64_t) gdt
	};
	lgdt (&gdt_ds);

	/* Init the globla thread context */
	lock_init (&tid_lock);
	list_init (&ready_list);
	list_init (&destruction_req);
	
    /*-- Alarm Clock ๊ณผ์ œ --*/
    list_init (&sleep_list); 
    /*-- Alarm Clock ๊ณผ์ œ --*/

	/* Set up a thread structure for the running thread. */
	initial_thread = running_thread ();
	init_thread (initial_thread, "main", PRI_DEFAULT);
	initial_thread->status = THREAD_RUNNING;
	initial_thread->tid = allocate_tid ();
}

์ฆ‰, thread.c์˜ ์ „์—ญ ๋ณ€์ˆ˜๋กœ ๋„ฃ์—ˆ๋˜ ์Šฌ๋ฆฝ ๋ฆฌ์ŠคํŠธ์˜ ์ดˆ๊ธฐํ™”๊ฐ€ ํฌํ•จ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ ๋‹ค์Œ, tid_t thread_create()๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

tid_t thread_create (const char *name, int priority,
		thread_func *function, void *aux) {
	struct thread *t;
	tid_t tid;

	ASSERT (function != NULL);

	/* Allocate thread. */
	t = palloc_get_page (PAL_ZERO);
	if (t == NULL)
		return TID_ERROR;

	/* Initialize thread. */
	init_thread (t, name, priority);
	tid = t->tid = allocate_tid ();

	/* Call the kernel_thread if it scheduled.
	 * Note) rdi is 1st argument, and rsi is 2nd argument. */
	t->tf.rip = (uintptr_t) kernel_thread;
	t->tf.R.rdi = (uint64_t) function;
	t->tf.R.rsi = (uint64_t) aux;
	t->tf.ds = SEL_KDSEG;
	t->tf.es = SEL_KDSEG;
	t->tf.ss = SEL_KDSEG;
	t->tf.cs = SEL_KCSEG;
	t->tf.eflags = FLAG_IF;

	/* Add to run queue. */
	thread_unblock (t);
    
    /*-- Alarm clock ๊ตฌํ˜„ --*/
	if(t->priority > thread_current()->priority)
		thread_yield();
    /*-- Alarm clock ๊ตฌํ˜„ --*/
    
	return tid;
}

์ถ”๊ฐ€ํ•œ ๋‚ด์šฉ ์ž์ฒด๋Š” ๋‘ ์ค„์— ๋ถˆ๊ณผํ•˜์ง€๋งŒ ์„ค๋ช…์ด ๋‹ค์†Œ ํ•„์š”ํ•œ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. Pintos์˜ alarm clock ๋‹จ๊ณ„์—์„œ๋Š” ๋‹จ์ˆœํ•œ sleep/wakeup ๊ธฐ๋Šฅ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์Šค๋ ˆ๋“œ ์šฐ์„ ์ˆœ์œ„ ๊ธฐ๋ฐ˜์˜ ์Šค์ผ€์ค„๋ง์ด ๋™์ž‘ํ•จ์„ ์ „์ œ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์Šค๋ ˆ๋“œ๋ณด๋‹ค ๋” ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ€์ง„ ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒˆ๋กœ ์ƒ์„ฑ๋˜์—ˆ์„ ๊ฒฝ์šฐ, ํ•ด๋‹น ์Šค๋ ˆ๋“œ๊ฐ€ ์ฆ‰์‹œ CPU๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์–‘๋ณด(yield)ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ thread_create() ๋กœ์ง์—์„œ๋Š” ์ƒˆ ์Šค๋ ˆ๋“œ๋ฅผ ready queue์— ์ถ”๊ฐ€(thread_unblock)ํ•œ ๋’ค ๋ฐ”๋กœ ํ˜„์žฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณ„์† ์‹คํ–‰๋˜์ง€๋งŒ, ์œ„์™€ ๊ฐ™์€ ์กฐ๊ฑด์„ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์ฆ‰์‹œ context switch๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค. ์ด๋กœ์จ ์šฐ์„ ์ˆœ์œ„ ๊ธฐ๋ฐ˜ ์„ ์  ์Šค์ผ€์ค„๋ง(priority preemption)๋„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ตฌํ˜„๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ฆ‰, ์ด ์ฝ”๋“œ๋Š” "์ƒˆ๋กœ ์ƒ์„ฑํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ํ˜„์žฌ ์Šค๋ ˆ๋“œ๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’๋‹ค๋ฉด, ์ž๋ฐœ์ ์œผ๋กœ ์–‘๋ณด(thread_yield)"ํ•˜๊ฒŒ ํ•˜์—ฌ ์ปค๋„์ด ์ƒˆ๋กœ ์ƒ์„ฑํ•œ high-priority thread๋ฅผ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ด ๋ถ€๋ถ„์ด ์—†์œผ๋ฉด, thread_create() ์ดํ›„ ๋ช…์‹œ์ ์ธ yield ๋˜๋Š” blocking์ด ์ผ์–ด๋‚˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์ƒˆ ์Šค๋ ˆ๋“œ๊ฐ€ ์‹คํ–‰๋  ๊ธฐํšŒ๋ฅผ ๊ฐ–์ง€ ๋ชปํ•˜๊ฒŒ ๋˜์–ด, ์„ ์ ํ˜• ์Šค์ผ€์ค„๋ง์ด ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

timer.c

timer_interrupt()๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

/* Timer interrupt handler. */
/* ๋งค tick๋งˆ๋‹ค timer ์ธํ„ฐ๋ŸฝํŠธ ์‹œ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜
     sleep queue์—์„œ ๊นจ์–ด๋‚  thread๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ.
*/
static void
timer_interrupt (struct intr_frame *args UNUSED) {
	ticks++; 
    thread_tick ();	

	if (get_next_tick_to_awake() <= ticks)
		thread_awake(ticks);
}

๋จผ์ €, thread.c์—์„œ ์ถ”๊ฐ€ํ–ˆ๋˜ get_next_tick_to_awake()์ด ์—ฌ๊ธฐ์„œ๋„ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” sleep list ์•ˆ์—์„œ ๊ฐ€์žฅ ๋นจ๋ฆฌ ๊นจ์–ด๋‚˜์•ผ ํ•  ์Šค๋ ˆ๋“œ์˜ wakeโ€‘up tick์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ—ฌํผ์ž…๋‹ˆ๋‹ค. 

๊ทธ๋ฆฌํ•˜์—ฌ ๋งค tick๋งˆ๋‹ค ticks(๋ถ€ํŒ… ์ดํ›„ ๋ˆ„์  tick) ๊ฐ’๊ณผ ๋น„๊ตํ•ด์„œ, “์ง€๊ธˆ ๊นจ์šธ ์Šค๋ ˆ๋“œ๊ฐ€ ์žˆ๋‹ˆ?”๋ฅผ O(1)๋กœ ํŒ๋ณ„ํ•ฉ๋‹ˆ๋‹ค. <=๋ฅผ ์“ฐ๋Š” ์ด์œ ๋Š” ๋™์ผ tick์— ๊นจ์›Œ์•ผ ํ•˜๋Š” ์Šค๋ ˆ๋“œ๋„ ํฌํ•จํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ sleep list์—์„œ wake_up_tick <= ticks์ธ ๋…ธ๋“œ๋ฅผ ์ „๋ถ€ ๋นผ์„œ ready list๋กœ ์˜ฎ๊ธฐ๊ณ , ๊ทธ ๊ณผ์ •์—์„œ next_tick_to_awake๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, timer_sleep()๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

/* Suspends execution for approximately TICKS timer ticks. */
void timer_sleep(int64_t ticks) {
	int64_t start = timer_ticks ();

	ASSERT (intr_get_level () == INTR_ON);
	// while (timer_elapsed (start) < ticks)
	// 	thread_yield ();
	thread_sleep(start + ticks);
}

๊ธฐ์กด ๋ฐฉ์‹์€ ์•ž์„œ ์–ธ๊ธ‰ํ•œ ๋ฐ”์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜์ •๋œ ๋ฐฉ์‹์€, โ‘  thread_sleep()๋กœ ํ˜„์žฌ ์Šค๋ ˆ๋“œ์˜ wake_up_tick ํ•„๋“œ์— start + ticks ์ €์žฅ ==> โ‘ก ์ž์‹ ์„ sleep list์— ์‚ฝ์ž… ==> โ‘ข next_tick_to_awake ๊ฐฑ์‹  ==> โ‘ฃ thread_block()์œผ๋กœ ๋ธ”๋กํ•˜์—ฌ, ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—๊ฒŒ CPU๋ฅผ ๋„˜๊ธฐ๋Š” ๊ณผ์ •์„ ๊ฑฐ์น˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


๊ฒฐ๊ณผ ํ™•์ธ

Pintos์˜ thread ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ๋‹ค์‹œ ์ปดํŒŒ์ผ ๋ฐ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด alarm ๊ด€๋ จํ•œ ๊ฒƒ๋“ค ์ค‘ alarm-priority๋ฅผ ์ œ์™ธํ•œ ํ•ญ๋ชฉ๋“ค์—์„œ ํ†ต๊ณผ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค (alarm-priority๋Š” priority scheduling ๊ตฌํ˜„์„ ํ†ตํ•ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ†ต๊ณผ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค).

(... ์ค‘๋žต ...)
make[1]: Leaving directory '/home/mihnsurk2/gitp/Mugen-Houyou/pintos-kaist/threads/build'
pintos -v -k -T 60 -m 20   -- -q   run alarm-single < /dev/null 2> tests/threads/alarm-single.errors > tests/threads/alarm-single.output
perl -I../.. ../../tests/threads/alarm-single.ck tests/threads/alarm-single tests/threads/alarm-single.result
pass tests/threads/alarm-single
pintos -v -k -T 60 -m 20   -- -q   run alarm-multiple < /dev/null 2> tests/threads/alarm-multiple.errors > tests/threads/alarm-multiple.output
perl -I../.. ../../tests/threads/alarm-multiple.ck tests/threads/alarm-multiple tests/threads/alarm-multiple.result
pass tests/threads/alarm-multiple
pintos -v -k -T 60 -m 20   -- -q   run alarm-simultaneous < /dev/null 2> tests/threads/alarm-simultaneous.errors > tests/threads/alarm-simultaneous.output
perl -I../.. ../../tests/threads/alarm-simultaneous.ck tests/threads/alarm-simultaneous tests/threads/alarm-simultaneous.result
pass tests/threads/alarm-simultaneous
pintos -v -k -T 60 -m 20   -- -q   run alarm-priority < /dev/null 2> tests/threads/alarm-priority.errors > tests/threads/alarm-priority.output
perl -I../.. ../../tests/threads/alarm-priority.ck tests/threads/alarm-priority tests/threads/alarm-priority.result
pass tests/threads/alarm-priority
pintos -v -k -T 60 -m 20   -- -q   run alarm-zero < /dev/null 2> tests/threads/alarm-zero.errors > tests/threads/alarm-zero.output
perl -I../.. ../../tests/threads/alarm-zero.ck tests/threads/alarm-zero tests/threads/alarm-zero.result
pass tests/threads/alarm-zero
pintos -v -k -T 60 -m 20   -- -q   run alarm-negative < /dev/null 2> tests/threads/alarm-negative.errors > tests/threads/alarm-negative.output
perl -I../.. ../../tests/threads/alarm-negative.ck tests/threads/alarm-negative tests/threads/alarm-negative.result
pass tests/threads/alarm-negative
pintos -v -k -T 60 -m 20   -- -q   run priority-change < /dev/null 2> tests/threads/priority-change.errors > tests/threads/priority-change.output
perl -I../.. ../../tests/threads/priority-change.ck tests/threads/priority-change tests/threads/priority-change.result
pass tests/threads/priority-change
(... ์ค‘๋žต ...)

๋งˆ์น˜๋ฉฐ

Alarm clock ํŒŒํŠธ๋Š” Pintos์˜ ์Šค๋ ˆ๋“œ ์ƒํƒœ ์ „์ด(thread_block, thread_unblock), ์Šค์ผ€์ค„๋ง, ์ธํ„ฐ๋ŸฝํŠธ ํ•ธ๋“ค๋ง, ๊ทธ๋ฆฌ๊ณ  ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜์˜ ๊ธฐ์ดˆ๋ฅผ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋งค์šฐ ์ค‘์š”ํ•œ ๊ณผ์ œ์˜€์Šต๋‹ˆ๋‹ค. Busy Waiting์ด๋ผ๋Š” ๋น„ํšจ์œจ์ ์ธ ๋ฐฉ์‹์„ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์˜ ํšจ์œจ์ ์ธ Sleep/Wakeup ๋ฉ”์ปค๋‹ˆ์ฆ˜์œผ๋กœ ๋ฐ”๊พธ๋Š” ๊ณผ์ •์„ ํ†ตํ•ด ์šด์˜์ฒด์ œ๊ฐ€ ์‹œ๊ฐ„์„ ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ•˜๊ณ  ์Šค๋ ˆ๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ์ œ์–ดํ•˜๋Š”์ง€ ๊ฐ์„ ์žก์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.  ๋‹ค์Œ ๊ธ€์—์„œ๋Š” ์šฐ์„ ์ˆœ์œ„ ์Šค์ผ€์ค„๋ง์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.