default pintOS๋ semaphore๋ฅผ ๋๊ธฐํ๊ณ ์๋ ์ค๋ ๋๋ค์ list์ waieters๊ฐ FIFO๋ก ๊ตฌํ๋์ด์๋ค. ์ด๋ฅผ ์ฌ๋ฌ ์ค๋ ๋๊ฐ lock, semaphore, condition variable์ ์ป๊ธฐ ์ํด ๊ธฐ๋ค๋ฆด ๊ฒฝ์ฐ ์ฐ์ ์์๊ฐ ๊ฐ์ฅ ๋์ thread๊ฐ CPU๋ฅผ ์ ์ ํ๋๋ก ์์ ํด๋ณด์.
๐ธ๐ธ๐ธ๋ธ๋ก๊ทธ ๊ธ์ ๋ณต์ต ๊ฒธ gitbook์ ์์์ ๋ฐ๋ผ ๋ฒ์ญ ๋ฐ ๊ด๋ จ ๋ด์ฉ์ JKํผ์ ์ ๋ฌ์ ์ ๋ฆฌํ ์์ ์ด๋ค. ๋ง๊ทธ๋๋ก JKํผ์ ์ธ ์นธํผ 100%๋ง๋ ๋ง์ ์๋ ์ ์๋ค๋ ๊ฑฐ ์ฐธ๊ณ ํด์ฃผ๊ธธ ๋ฐ๋๋ค.
gitbook์ ๋ค์ ์ฝ์ด๋ณด๋ ์ค์ํ ๋ด์ฉ๋ค๊ณผ ํํธ๋ค์ด ์ด์์ ๋ค ์์๋ค. ๋ ๋จผ ์ฅ๋์ด๋ผ๋๊ฒ ์ด๋ฐ๊ฑด๊ฐ!
์ค๋ ๋ ๊ฐ์ ์์ ๊ณต์ ๊ฐ ์ ์คํ๊ณ ํต์ ๋ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌ๋์ง ์์ผ๋ฉด ์ผ๋ฐ์ ์ผ๋ก ํฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค. ํนํ ์ด์ ์ฒด์ ์ปค๋์์๋ ์๋ชป๋ ๊ณต์ ๋ก ์ธํด ์ ์ฒด ์์คํ ์ด ์ถฉ๋ํ ์ ์์ต๋๋ค. Pintos๋ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ฌ๋ฌ ๋๊ธฐํ ๊ธฐ๋ณธ ์์๋ฅผ ์ ๊ณตํฉ๋๋ค.
๊ฐ์ฅ ์์์ ์ธ ๋๊ธฐํ ๋ฐฉ๋ฒ์ ์ธํฐ๋ฝํธ๋ฅผ ๋นํ์ฑํํ๋ ๊ฒ์ ๋๋ค. ์ฆ, CPU๊ฐ ์ธํฐ๋ฝํธ์ ์๋ตํ์ง ๋ชปํ๋๋ก ์ผ์์ ์ผ๋ก ๋ง๋ ๊ฒ์ ๋๋ค. ์ธํฐ๋ฝํธ๊ฐ ๊บผ์ ธ ์์ผ๋ฉด ๋ค๋ฅธ ์ค๋ ๋๋ ์คํ ์ค์ธ ์ค๋ ๋๋ฅผ ์ ์ ํ์ง ์์ต๋๋ค. ์๋ํ๋ฉด ์ค๋ ๋ ์ ์ ์ ํ์ด๋จธ ์ธํฐ๋ฝํธ์ ์ํด ์ ์ด๋๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ธํฐ๋ฝํธ๊ฐ ์ผ์ ธ ์๋ ๊ฒฝ์ฐ, ์ผ๋ฐ์ ์ผ๋ก ๊ทธ๋ ์ต๋๋ค. ์คํ ์ค์ธ ์ค๋ ๋๋ ๋ ๊ฐ์ C ๋ฌธ ์ฌ์ด ๋๋ ํ๋์ ์คํ ๋ด์์๋ ์ธ์ ๋ ์ง ๋ค๋ฅธ ์ค๋ ๋์ ์ํด ์ ์ ๋ ์ ์์ต๋๋ค.
๊ทธ๋ฐ ๊ด์ ์์ Pintos๋ "์ ์ ๊ฐ๋ฅํ ์ปค๋(preemptible kernel)" ์ ๋๋ค. ์ฆ, ์ปค๋ ์ค๋ ๋๋ ์ธ์ ๋ ์ง ์ ์ ๋ ์ ์์ต๋๋ค. ์ ํต์ ์ธ Unix ์์คํ ์ "๋น์ ์ ๊ฐ๋ฅํ(nonpreemptible)" ์ปค๋์ด๋ผ๊ณ ํ ์ ์์ต๋๋ค. ์ฆ, ์ปค๋ ์ค๋ ๋๋ ๋ช ์์ ์ผ๋ก ์ค์ผ์ค๋ฌ๋ฅผ ํธ์ถํ๋ ์ง์ ์์๋ง ์ ์ ๋ ์ ์์ต๋๋ค. (์ฌ์ฉ์ ํ๋ก๊ทธ๋จ์ ๋ ๋ชจ๋ธ ๋ชจ๋์์ ์ธ์ ๋ ์ง ์ ์ ๋ ์ ์์ต๋๋ค.) ์ ์ ๊ฐ๋ฅํ ์ปค๋์ ๋ช ์์ ์ธ ๋๊ธฐํ๊ฐ ๋ ํ์ํ๋ค๋ ์ ์์ ์์ํ ์ ์์ต๋๋ค.
์ฌ๋ฌ๋ถ์ด ์ธํฐ๋ฝํธ ์ํ๋ฅผ ์ง์ ์ธํ ํ ํ์๋ ๋ณ๋ก ์์ต๋๋ค. ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์ด์ด์ง๋ ์น์ ์์ ์ค๋ช ํด๋๋ฆด synchronization ํจ์๋ค์ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค. ์ธํฐ๋ฝํธ๋ฅผ ๋นํ์ฑํ ์ํค๋ ์ฃผ๋ ์ด์ ๋ ์ธ๋ถ์ ์ธํฐ๋ฝํธ ํธ๋ค๋ฌ์ ์ปค๋ ์ฐ๋ ๋๋ฅผ ๋๊ธฐํ ์ํค๊ธฐ ์ํด์ ์ ๋๋ค. ์ธ๋ถ ์ธํฐ๋ฝํธ ํธ๋ค๋ฌ๋ ์ ๋ค๊ฒ ํ ์ ์์ด์, ๋นํ์ฑํ์ํค์ง ์๊ณ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก๋ ๋๊ธฐํ๊ฐ ๊ฑฐ์ ๋ถ๊ฐ๋ฅ ํฉ๋๋ค.
๋ช๊ฐ์ง ์ธ๋ถ ์ธํฐ๋ฝํธ๋ ์ธํฐ๋ฝํธ ๋นํ์ฑํ๋ก๋ ๋ง์๋ ์ ์์ต๋๋ค. ์ด๋ฐ ์ธํฐ๋ฝํธ๋ค์ ๋ง์คํฌ ๋ถ๊ฐ๋ฅ ์ธํฐ๋ฝํธ(NMIs) ๋ผ๊ณ ํ๋๋ฐ, ์ปดํจํฐ๊ฐ ๋ถ์ด๋ฌ์ ๋(ํ์ฌ์ํฉ)์ฒ๋ผ ๊ธด๊ธํ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉ๋์ด์ผ ํฉ๋๋ค. Pintos๋ ๋ง์คํฌ ๋ถ๊ฐ๋ฅ ์ธํฐ๋ฝํธ๋ฅผ ๋ค๋ฃจ์ง ์์ต๋๋ค.
์ธํฐ๋ฝํธ๋ฅผ ๋นํ์ฑํํ๊ณ ํ์ฑํํ๊ธฐ ์ํ ์ ํ๊ณผ ํจ์๋ include/threads/interrupt.h์ ์ ์๋์ด ์์ต๋๋ค.
INTR_OFF ๋๋ INTR_ON ์ค ํ๋๋ก, ๊ฐ๊ฐ ์ธํฐ๋ฝํธ๊ฐ ๋นํ์ฑํ๋์๊ฑฐ๋ ํ์ฑํ๋์์์ ๋ํ๋ ๋๋ค.
enum intr_level;
ํ์ฌ ์ธํฐ๋ฝํธ ์ํ(inter_level)๋ฅผ ๋ฆฌํดํ๋ ํจ์์ ๋๋ค.
enum intr_level intr_get_level (void)
ํ์ฌ ์ํ(inter_level)์ ๋ฐ๋ผ ์ธํฐ๋ฝํธ๋ฅผ ํ์ฑํํ๊ฑฐ๋ ๋นํ์ฑํํ๋ ํจ์์ ๋๋ค. ์ธํฐ๋ฝํธ์ ์ด์ ์ํ๋ฅผ ๋ฆฌํดํฉ๋๋ค.
enum intr_level intr_set_level (enum intr_level level);
์ธํฐ๋ฝํธ๋ฅผ ํ์ฑํํ๋ ํจ์์ ๋๋ค. ์ธํฐ๋ฝํธ์ ์ด์ ์ํ๋ฅผ ๋ฆฌํดํฉ๋๋ค.
enum intr_level intr_enable (void);
์ธํฐ๋ฝํธ๋ฅผ ๋นํ์ฑํ ํด์ฃผ๋ ํจ์์ ๋๋ค. ์ธํฐ๋ฝํธ์ ์ด์ ์ํ๋ฅผ ๋ฆฌํดํฉ๋๋ค.
enum intr_level intr_disable (void);
์ธ๋งํฌ์ด๋ 0 ์ด์์ ์ ์์ ํจ๊ป ์ฌ์ฉ๋๋ ๋ ๊ฐ์ง atomicํ(race condition์ด ์๋๋ก ํ๊ฐ์ง๋ง ์ ๊ทผํ๋๋ก ํ) ์กฐ์ ์ฐ์ฐ์ธ "Down" ๋๋ "P"์ "UP" ๋๋ "V"๋ก ๊ตฌ์ฑ๋ฉ๋๋ค. ์ด ์ฐ์ฐ์๋ค์ ๋ค์๊ณผ ๊ฐ์ ์ญํ ์ ์ํํฉ๋๋ค:
0์ผ๋ก ์ด๊ธฐํ๋ ์ธ๋งํฌ์ด๋ ์ ํํ ํ ๋ฒ ๋ฐ์ํ ์ด๋ฒคํธ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ๋ฐ ์ฌ์ฉ๋ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์ค๋ ๋ A๊ฐ ๋ค๋ฅธ ์ค๋ ๋ B๋ฅผ ์์ํ๊ณ B๊ฐ ์ด๋ค ์์ ์ด ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ค๊ณ ํ ๋, A๋ 0์ผ๋ก ์ด๊ธฐํ๋ ์ธ๋งํฌ์ด๋ฅผ ์์ฑํ๊ณ B์๊ฒ ์ ๋ฌํ ํ ์ธ๋งํฌ์ด๋ฅผ "down"ํฉ๋๋ค. B๊ฐ ์์ ์ ์๋ฃํ๋ฉด ์ธ๋งํฌ์ด๋ฅผ "up"ํฉ๋๋ค. A๊ฐ ์ธ๋งํฌ์ด๋ฅผ "down"ํ๊ฑฐ๋ B๊ฐ "up"ํ๋ ์์์ ์๊ด์์ด ์ด ๋ฐฉ๋ฒ์ ์๋ํฉ๋๋ค.
์์:
struct semaphore sema;
/* Thread A */
void threadA (void) {
sema_down (&sema);
}
/* Thread B */
void threadB (void) {
sema_up (&sema);
}
/* main function */
void main (void) {
sema_init (&sema, 0);
thread_create ("threadA", PRI_MIN, threadA, NULL);
thread_create ("threadB", PRI_MIN, threadB, NULL);
}
์ด ์์์์, threadA๋ sema_down() ํจ์๋ฅผ ์ฌ์ฉํ์ฌ threadB๊ฐ sema_up() ํจ์๋ฅผ ํธ์ถํ ๋๊น์ง ์คํ์ ๋ฉ์ถฅ๋๋ค.
1๋ก ์ด๊ธฐํ๋ ์ธ๋งํฌ์ด๋ ์ฃผ๋ก ๋ฆฌ์์ค ์ ๊ทผ์ ์ ์ดํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ๋ฆฌ์์ค๋ฅผ ์ฌ์ฉํ๊ธฐ ์ ์ ํด๋นํ๋ ์ฝ๋ ๋ธ๋ก์ ์ธ๋งํฌ์ด๋ฅผ "down" (๊ฐ์)์ํค๊ณ , ๋ฆฌ์์ค ์ฌ์ฉ์ด ๋๋๋ฉด "up" (์ฆ๊ฐ)์ํต๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ์๋ ๋ฝ (lock)์ด ๋ ์ ํฉํ ์ ์์ต๋๋ค.
์ธ๋งํฌ์ด๋ 1๋ณด๋ค ํฐ ๊ฐ์ ๊ฐ์ง๋๋ก ์ด๊ธฐํ๋ ์๋ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ฌํ ๊ฒฝ์ฐ๋ ๋๋ฌผ๊ฒ ์ฌ์ฉ๋ฉ๋๋ค. ์ธ๋งํฌ์ด๋ Edsger Dijkstra๊ฐ ๊ฐ๋ฐํ ๊ฒ์ผ๋ก, THE ์ด์ ์ฒด์ ์์ ์ฒ์ ์ฌ์ฉ๋์์ต๋๋ค. Pintos์ ์ธ๋งํฌ์ด ํ์ ๊ณผ ์ฐ์ฐ์ include/threads/synch.h ํค๋ ํ์ผ์ ์ ์ธ๋์ด ์์ต๋๋ค.
์ธ๋งํฌ์ด๋ฅผ ๋ํ๋ ๋๋ค.
struct semaphore;
์ฃผ์ด์ง ์ด๊ธฐ๊ฐ์ ๊ฐ์ง๊ณ ์๋ก์ด ์ธ๋งํฌ์ด๋ฅผ ์ด๊ธฐํํฉ๋๋ค.
void sema_init (struct semaphore *sema, unsigned value);
sema์ ๋ํด "down" ๋๋ "P" ์ฐ์ฐ์ ์ํํ์ฌ ๊ฐ์ด ์์๊ฐ ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๊ณ , ๊ทธ ํ์ ๊ฐ์ 1 ๊ฐ์์ํต๋๋ค.
void sema_down (struct semaphore *sema);
sema์ ๋ํด "down" ๋๋ "P" ์ฐ์ฐ์ ์๋ํ๋ฉฐ, ๋๊ธฐํ์ง ์์ต๋๋ค. sema๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๊ฐ์๋๋ฉด true๋ฅผ ๋ฐํํ๊ณ , ์ด๋ฏธ 0์ด๊ธฐ ๋๋ฌธ์ ๋๊ธฐ ์์ด ๊ฐ์ํ ์ ์๋ ๊ฒฝ์ฐ false๋ฅผ ๋ฐํํฉ๋๋ค. ์ด ํจ์๋ฅผ ๋ฐ๋ณต๋ฌธ ์์์ ๊ณ์ ํธ์ถํ๋ ๊ฒ์ CPU ์๊ฐ์ ๋ญ๋นํ๋ฏ๋ก, sema_down()๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ ๋ค๋ฅธ ์ ๊ทผ ๋ฐฉ์์ ์ฐพ๋ ๊ฒ์ด ์ข์ต๋๋ค.
bool sema_try_down (struct semaphore *sema);
sema์ ๋ํด "up" ๋๋ "V" ์ฐ์ฐ์ ์คํํ์ฌ ๊ฐ์ ์ฆ๊ฐ์ํต๋๋ค. sema์ ๋๊ธฐ ์ค์ธ ์ค๋ ๋๊ฐ ์๋ค๋ฉด ๊ทธ ์ค ํ๋๋ฅผ ๊นจ์๋๋ค. ๋๋ถ๋ถ์ ๋๊ธฐํ ๊ธฐ๋ฒ๊ณผ๋ ๋ฌ๋ฆฌ, sema_up()์ ์ธ๋ถ ์ธํฐ๋ฝํธ ํธ๋ค๋ฌ ๋ด์์ ํธ์ถ๋ ์ ์์ต๋๋ค.
void sema_up (struct semaphore *sema);
์ธ๋งํฌ์ด๋ ์ธํฐ๋ฝํธ ๋นํ์ฑํ(disabling interrupt) ๋ฐ ์ค๋ ๋์ ์ฐจ๋จ ๋ฐ ์ฐจ๋จ ํด์ (thread_block() ๋ฐ thread_unblock())๋ฅผ ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉํ์ฌ ๊ตฌ์ถ๋ฉ๋๋ค. ๊ฐ ์ธ๋งํฌ์ด๋ lib/kernel/list.c์ ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๊ตฌํ์ ์ฌ์ฉํ์ฌ ๋๊ธฐ ์ค์ธ ์ค๋ ๋์ ๋ชฉ๋ก์ ์ ์งํฉ๋๋ค.
๋ฝ์ ์ด๊ธฐ ๊ฐ์ด 1์ธ ์ธ๋งํฌ์ด์ ์ ์ฌํฉ๋๋ค(์ธ๋งํฌ์ด ์ฐธ์กฐ). ๋ฝ์ "up"๊ณผ ๋๋ฑํ ์์ ์ "release"๋ก ๋ถ๋ฆฌ๋ฉฐ, "down" ์์ ์ "acquire"๋ก ๋ถ๋ฆฝ๋๋ค.
์ธ๋งํฌ์ด์ ๋น๊ตํ์ฌ ๋ฝ์ ์ถ๊ฐ์ ์ธ ์ ํ์ด ์์ต๋๋ค: ์ค์ง, ๋ฝ์ ๊ฐ๊ณ ์๋ โownerโ ์ฐ๋ ๋๋ง์ด ๋ฝ์ ๋์์ค ์ ์์ต๋๋ค. ๋ง์ฝ ์ด ์ ์ฝ๋๋ฌธ์ ๋ฌธ์ ๊ฐ ๋๋ ๊ฒฝ์ฐ๋ ๋ฝ๋์ ์ ์ธ๋งํฌ์ด๋ฅผ ์จ์ผํ๋ค๋ ์ข์ ์ ํธ์ ๋๋ค.
Pintos์์์ ๋ฝ์ "์ฌ๊ท์ (recursive)"์ด ์๋๋๋ค. ์ฆ, ํ์ฌ ๋ฝ์ ์์ ํ ์ค๋ ๋๊ฐ ํด๋น ๋ฝ์ ๋ค์ ํ๋ํ๋ ค๊ณ ์๋ํ๋ ๊ฒ์ ์ค๋ฅ์ ๋๋ค. ๋ฝ์ ์ ํ๊ณผ ํจ์๋ include/threads/synch.h์ ์ ์ธ๋์ด ์์ต๋๋ค.
๋ฝ์ ๋ํ๋ ๋๋ค.
struct lock;
lock์ ์๋ก ์ด๊ธฐํํฉ๋๋ค. ์ด ๋ฝ์ ์ด๊ธฐ ์ํ์์ ์ด๋ค ์ค๋ ๋์๊ฒ๋ ์์ ๋์ง ์์ต๋๋ค.
void lock_init (struct lock *lock);
ํ์ฌ ์ค๋ ๋๊ฐ ๋ฝ์ ํ๋ํฉ๋๋ค. ํ์ํ ๊ฒฝ์ฐ ํ์ฌ ์์ ์๊ฐ ๋ฝ์ ํด์ ํ ๋๊น์ง ๋๊ธฐํฉ๋๋ค.
void lock_acquire (struct lock *lock);
ํ์ฌ ์ค๋ ๋๊ฐ ์ฌ์ฉํ๊ธฐ ์ํด ๋ฝ์ ํ๋ํ๋ ค๊ณ ์๋ํฉ๋๋ค. ๋๊ธฐํ์ง ์๊ณ ๋ฐ๋ก ํ๋ํ ์ ์๋ ๊ฒฝ์ฐ true๋ฅผ ๋ฐํํ๊ณ , ์ด๋ฏธ ๋ค๋ฅธ ์ค๋ ๋์ ์ํด ์์ ๋์์ ๊ฒฝ์ฐ false๋ฅผ ๋ฐํํฉ๋๋ค. ์ด ํจ์๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก ํธ์ถํ๋ ๊ฒ์ CPU ์๊ฐ์ ๋ญ๋นํ๋ฏ๋ก lock_acquire()๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
bool lock_try_acquire (struct lock *lock);
ํ์ฌ ์ค๋ ๋๊ฐ ์์ ํ ๋ฝ์ ํด์ ํฉ๋๋ค.
void lock_release (struct lock *lock);
running thread๊ฐ lock์ ์์ ํ๋ฉด true๋ฅผ ๋ฐํํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด false๋ฅผ ๋ฐํํฉ๋๋ค. ์์์ ์ค๋ ๋๊ฐ lock์ ์์ ํ๋์ง ํ ์คํธํ๋ ํจ์๋ ์์ต๋๋ค. ์๋ํ๋ฉด ํธ์ถ์๊ฐ ๊ทธ ๊ฒฐ๊ณผ์ ๋ํ ์กฐ์น๋ฅผ ์ทจํ๊ธฐ ์ ์ ์ํฉ์ด ๋ณ๊ฒฝ๋ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
bool lock_held_by_current_thread (const struct lock *lock):
๋ชจ๋ํฐ๋ ์ธ๋งํฌ์ด๋ ๋ฝ๋ณด๋ค ๋ ๋์ ์์ค์ ๋๊ธฐํ ํ์์ ๋๋ค. ๋ชจ๋ํฐ๋ ๋๊ธฐํ๋๋ ๋ฐ์ดํฐ์ ๋ชจ๋ํฐ ๋ฝ์ด๋ผ๊ณ ๋ถ๋ฆฌ๋ ๋ฝ, ๊ทธ๋ฆฌ๊ณ ํ๋ ์ด์์ ์กฐ๊ฑด ๋ณ์๋ก ๊ตฌ์ฑ๋ฉ๋๋ค. ์ค๋ ๋๊ฐ ๋ณดํธ๋ ๋ฐ์ดํฐ์ ์ ๊ทผํ๊ธฐ ์ ์ ๋จผ์ ๋ชจ๋ํฐ ๋ฝ์ ํ๋ํฉ๋๋ค. ์ด๋ ์ค๋ ๋๋ "๋ชจ๋ํฐ ์์ ์๋ค"๊ณ ๋งํฉ๋๋ค. ๋ชจ๋ํฐ ์์ ์๋ ๋์ ์ค๋ ๋๋ ๋ณดํธ๋ ๋ชจ๋ ๋ฐ์ดํฐ์ ๋ํ ์ ์ด๊ถ์ ๊ฐ์ง๋ฉฐ, ์์ ๋กญ๊ฒ ๊ฒ์ฌํ๊ฑฐ๋ ์์ ํ ์ ์์ต๋๋ค. ๋ณดํธ๋ ๋ฐ์ดํฐ์ ๋ํ ์ ๊ทผ์ด ์๋ฃ๋๋ฉด ๋ชจ๋ํฐ ๋ฝ์ ํด์ ํฉ๋๋ค.
์ปจ๋์ ๋ณ์๋ ํน์ ์ปจ๋์ (์กฐ๊ฑด)์ด ์ฐธ์ด ๋ ๋๊น์ง ๋ชจ๋ํฐ์์ ์ฝ๋๊ฐ ๊ธฐ๋ค๋ฆด ์ ์๊ฒํฉ๋๋ค. ๊ฐ ์ปจ๋์ ๋ณ์๋ ์ถ์์ ์ธ ์กฐ๊ฑด๊ณผ ๊ด๋ จ์์ต๋๋ค. ์๋ฅผ ๋ค๋ฉด โํ๋ก์ธ์ค๊ฐ ์งํ๋๋ ๋์ ๋ช๊ฐ์ง ๋ฐ์ดํฐ๊ฐ ๋์ฐฉํ๋ ์กฐ๊ฑดโ, โ์ ์ ์ ๋ง์ง๋ง ํค๋ณด๋ ์ ๋ ฅ์ผ๋ก ๋ถํฐ 10์ด๊ฐ ๋๊ฒ ์ง๋๋ ์กฐ๊ฑดโ ์ ๋๋ค. ๋ชจ๋ํฐ์์ ์ฝ๋๊ฐ ํน์ ์กฐ๊ฑด์ด ์ฐธ์ด ๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆด ๋, ๊ทธ ์ฝ๋๋ ์ํ๋ ์กฐ๊ฑด๊ณผ ๊ด๋ จ๋ ์ปจ๋์ ๋ณ์๋ฅผ ๊ธฐ๋ค๋ฆฝ๋๋ค(wait). ๊ทธ ์ปจ๋์ ๋ณ์๋ ๋ฝ์ ๋์์ฃผ๊ณ ์ปจ๋์ ์ด ์ ํธ๋ฐ๋ ๊ฒ์ ๊ธฐ๋ค๋ฆฝ๋๋ค. ๋ฐ๋๋ก ์ด ์ฝ๋๊ฐ ํน์ ์กฐ๊ฑด์ด ์ฐธ์ด๋๋๋ก ๋ง๋๋ ๊ฒฝ์ฐ์๋, ๊ทธ ์ฐธ์ด๋ ์กฐ๊ฑด์ ์ ํธ๋ก ๋ณด๋ด์(signal) ๊ธฐ๋ค๋ฆฌ๋ ์ฝ๋ ํ๋๋ฅผ ๊นจ์ฐ๊ฑฐ๋, ์ ๋ถํํ ์๋ ค์(broadcast) ์๋ ์ฝ๋๋ฅผ ์ ๋ถ ๊นจ์๋๋ค.
๋ชจ๋ํฐ์ ์ด๋ก ์ ์ธ ๊ธฐ๋ฐ์ C.A.R.ํธ์ด๊ฐ ๋ง๋ จํ์ต๋๋ค. ์ค์ ์ ์ธ ์ฌ์ฉ๋ฒ์ ๋์ค์ Mesa ์ด์ ์ฒด์ ์ ๊ดํ ๋ ผ๋ฌธ์์ ๋ ๋ฐ์ ๋์์ต๋๋ค. ์กฐ๊ฑด ๋ณ์์ ํ์ ๊ณผ ํจ์๋ include/threads/synch.h์ ์ ์ธ๋์ด ์์ต๋๋ค.
์กฐ๊ฑด ๋ณ์๋ฅผ ๋ํ๋ ๋๋ค.
struct condition;
cond๋ฅผ ์๋ก์ด ์กฐ๊ฑด ๋ณ์๋ก ์ด๊ธฐํํฉ๋๋ค.
void cond_init (struct condition *cond);
์์์ ์ผ๋ก lock(๋ชจ๋ํฐ ๋ฝ)์ ํด์ ํ๊ณ ๋ค๋ฅธ ์ฝ๋์์ cond(์กฐ๊ฑด ๋ณ์)๊ฐ ์๊ทธ๋์ ๋ฐ์ ๋๊น์ง ๋๊ธฐํฉ๋๋ค. cond๊ฐ ์๊ทธ๋์ ๋ฐ์ ํ, ๋ฐํํ๊ธฐ ์ ์ ๋ค์ lock์ ์ป์ต๋๋ค. ์ด ํจ์๋ฅผ ํธ์ถํ๊ธฐ ์ ์ lock์ ๋ณด์ ํด์ผ ํฉ๋๋ค. ์๊ทธ๋์ ๋ณด๋ด๊ณ ๋๊ธฐ์์ ๊นจ์ด๋๋ ๊ฒ์ ์์์ ์ธ ์ฐ์ฐ์ด ์๋๋๋ค. ๋ฐ๋ผ์ ์ผ๋ฐ์ ์ผ๋ก cond_wait() ํธ์ถ์๋ ๋๊ธฐ๊ฐ ์๋ฃ๋ ํ ์กฐ๊ฑด์ ๋ค์ ํ์ธํ๊ณ ํ์ํ ๊ฒฝ์ฐ ๋ค์ ๋๊ธฐํด์ผ ํฉ๋๋ค. ์์ ๋ ๋ค์ ์น์ ์์ ์ค๋ช ํฉ๋๋ค.
void cond_wait (struct condition *cond, struct lock *lock);
๋ง์ฝ cond(๋ชจ๋ํฐ ๋ฝ lock์ผ๋ก ๋ณดํธ๋๋)์ ๋๊ธฐ ์ค์ธ ์ค๋ ๋๊ฐ ์๋ค๋ฉด, ์ด ํจ์๋ ๊ทธ ์ค ํ๋๋ฅผ ๊นจ์๋๋ค. ๋๊ธฐ ์ค์ธ ์ค๋ ๋๊ฐ ์๋ค๋ฉด ์ด๋ค ๋์๋ ์ํํ์ง ์๊ณ ๋ฐํํฉ๋๋ค. ์ด ํจ์๋ฅผ ํธ์ถํ๊ธฐ ์ ์ lock์ ๋ณด์ ํด์ผ ํฉ๋๋ค.
void cond_signal (struct condition *cond, struct lock *lock);
cond(๋ชจ๋ํฐ ๋ฝ lock์ผ๋ก ๋ณดํธ๋๋)์ ๋๊ธฐ ์ค์ธ ๋ชจ๋ ์ค๋ ๋(์๋ ๊ฒฝ์ฐ)๋ฅผ ๊นจ์๋๋ค. ์ด ํจ์๋ฅผ ํธ์ถํ๊ธฐ ์ ์ lock์ ๋ณด์ ํด์ผ ํฉ๋๋ค.
void cond_broadcast (struct condition *cond, struct lock *lock);
๋ชจ๋ํฐ์ ์ ํ์ ์ธ ์๋ ํ๋ ์ด์์ "์์ฐ์" ์ค๋ ๋๊ฐ ๋ฌธ์๋ฅผ ์์ฑํ๊ณ ํ๋ ์ด์์ "์๋น์" ์ค๋ ๋๊ฐ ๋ฌธ์๋ฅผ ์ฝ๋ ๋ฒํผ ์ฒ๋ฆฌ์ ๋๋ค. ์ด๋ฅผ ๊ตฌํํ๊ธฐ ์ํด์๋ ๋ชจ๋ํฐ ๋ฝ ์ธ์๋ not_full๊ณผ not_empty๋ผ๊ณ ๋ถ๋ฅผ ๋ ๊ฐ์ง ์กฐ๊ฑด ๋ณ์๊ฐ ํ์ํฉ๋๋ค.
char buf[BUF_SIZE]; /* Buffer. */
size_t n = 0; /* 0 <= n <= BUF SIZE: # of characters in buffer. */
size_t head = 0; /* buf index of next char to write (mod BUF SIZE). */
size_t tail = 0; /* buf index of next char to read (mod BUF SIZE). */
struct lock lock; /* Monitor lock. */
struct condition not_empty; /* Signaled when the buffer is not empty. */
struct condition not_full; /* Signaled when the buffer is not full. */
...initialize the locks and condition variables...
void put (char ch) {
lock_acquire (&lock);
while (n == BUF_SIZE) /* Can't add to buf as long as it's full. */
cond_wait (¬_full, &lock);
buf[head++ % BUF_SIZE] = ch; /* Add ch to buf. */
n++;
cond_signal (¬_empty, &lock); /* buf can't be empty anymore. */
lock_release (&lock);
}
char get (void) {
char ch;
lock_acquire (&lock);
while (n == 0) /* Can't read buf as long as it's empty. */
cond_wait (¬_empty, &lock);
ch = buf[tail++ % BUF_SIZE]; /* Get ch from buf. */
n--;
cond_signal (¬_full, &lock); /* buf can't be full anymore. */
lock_release (&lock);
}
์์ ์ฝ๋๊ฐ ์์ ํ ์ ํํ๋ ค๋ฉด BUF_SIZE๊ฐ SIZE_MAX + 1๋ก ๋๋์ด ๋จ์ด์ ธ์ผ ํจ์ ์ ์ํ์ธ์. ๊ทธ๋ ์ง ์์ผ๋ฉด head๊ฐ 0์ผ๋ก ๋๋์๊ฐ ๋ ์ฒ์์ผ๋ก ์คํจํ ์ ์์ต๋๋ค. ์ค์ ๋ก๋ BUF_SIZE๊ฐ ๋ณดํต 2์ ๊ฑฐ๋ญ์ ๊ณฑ์๊ฐ ๋ ๊ฒ์ ๋๋ค.
์ต์ ํ ์ฅ๋ฒฝ(optimization barrier) ์ ์ปดํ์ผ๋ฌ๊ฐ ์ฅ๋ฒฝ์ ํต๊ณผํ๋ ๋ฉ๋ชจ๋ฆฌ ์ํ์ ๋ํด ๊ฐ์ ์ ํ ์ ์๋๋ก ํ๋ ํน๋ณํ ๋ฌธ์ฅ์ ๋๋ค. ์ปดํ์ผ๋ฌ๋ ์ฅ๋ฒฝ์ ํต๊ณผํ๋ ๋ณ์์ ์ฝ๊ธฐ ๋๋ ์ฐ๊ธฐ๋ฅผ ์ฌ๋ฐฐ์ดํ์ง ์๊ฑฐ๋, ๋ณ์์ ๊ฐ์ด ์ฅ๋ฒฝ์ ํต๊ณผํ๋ ๋์ ๋ณ๊ฒฝ๋์ง ์์๋ค๊ณ ๊ฐ์ ํ์ง ์์ต๋๋ค. ๋ค๋ง, ์ฃผ์๊ฐ ์ฌ์ฉ๋์ง ์๋ ๋ก์ปฌ ๋ณ์์ ๋ํด์๋ ์์ธ์ ๋๋ค. Pintos์์๋ include/threads/synch.h์์ barrier() ๋งคํฌ๋ก๋ฅผ ์ต์ ํ ์ฅ๋ฒฝ์ผ๋ก ์ ์ํ๊ณ ์์ต๋๋ค.
์ต์ ํ ์ฅ๋ฒฝ์ ์ฌ์ฉํ๋ ์ด์ ์ค ํ๋๋ ๋ฐ์ดํฐ๊ฐ ๋น๋๊ธฐ์ ์ผ๋ก ๋ณ๊ฒฝ๋ ์ ์๋ ๊ฒฝ์ฐ์ ๋๋ค. ๋ค๋ฅธ ์ค๋ ๋๋ ์ธํฐ๋ฝํธ ํธ๋ค๋ฌ์ ๊ฐ์ ์์์ ์ํด ์ปดํ์ผ๋ฌ์ ์์ง ๋ชปํ๋ ์ํฉ์์ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋ ์ ์์ต๋๋ค. devices/timer.c์ too_many_loops() ํจ์๊ฐ ์ด๋ฅผ ์๋ก ๋ค ์ ์์ต๋๋ค. ์ด ํจ์๋ ํ์ด๋จธ ํฑ์ด ๋ฐ์ํ ๋๊น์ง ๋ฃจํ๋ฅผ ๋๋ฉด์ busy-waiting์ผ๋ก ์์๋ฉ๋๋ค.
/* Wait for a timer tick. */
int64_t start = ticks;
while (ticks == start)
barrier();
๋ฃจํ์์ ์ต์ ํ ์ฅ๋ฒฝ์ด ์๋ ๊ฒฝ์ฐ, ์ปดํ์ผ๋ฌ๋ start์ ticks๊ฐ ์ฒ์์ ๋์ผํ๋ฉฐ ๋ฃจํ ์์ฒด์์ ๋ณ๊ฒฝ๋์ง ์๊ธฐ ๋๋ฌธ์ ๋ฃจํ๊ฐ ์ข ๋ฃ๋์ง ์์ ๊ฒ์ด๋ผ๊ณ ๊ฒฐ๋ก ์ ๋ด๋ฆด ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ฉด ์ปดํ์ผ๋ฌ๋ ํจ์๋ฅผ ๋ฌดํ ๋ฃจํ๋ก "์ต์ ํ"ํ ์ ์์ผ๋ฉฐ, ์ด๋ ๋ถ๋ช ํ ์ํ์ง ์๋ ๊ฒฐ๊ณผ์ ๋๋ค.
์ต์ ํ ์ฅ๋ฒฝ์ ๋ค๋ฅธ ์ปดํ์ผ๋ฌ ์ต์ ํ๋ฅผ ํผํ๊ธฐ ์ํด ์ฌ์ฉ๋ ์๋ ์์ต๋๋ค. devices/timer.c์ ์๋ busy_wait() ํจ์๊ฐ ์ด๋ฅผ ์๋ก ๋ค ์ ์์ต๋๋ค. ์ด ํจ์์๋ ๋ค์๊ณผ ๊ฐ์ ๋ฃจํ๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
while (loops-- > 0)
barrier ();
์ด ๋ฃจํ์ ๋ชฉํ๋ ์๋ ๊ฐ๋ถํฐ 0๊น์ง ๋ฃจํ๋ฅผ ์นด์ดํธํ์ฌ ๋ฐ์ ๋๊ธฐ(busy-wait)๋ฅผ ์ํํ๋ ๊ฒ์ ๋๋ค. ์ต์ ํ ์ฅ๋ฒฝ์ด ์๋ ๊ฒฝ์ฐ ์ปดํ์ผ๋ฌ๋ ์ ์ฉํ ์ถ๋ ฅ์ ์์ฑํ์ง ์์ผ๋ฉฐ ๋ถ์์ฉ๋ ์๊ธฐ ๋๋ฌธ์ ๋ฃจํ๋ฅผ ์์ ํ ์ญ์ ํ ์ ์์ต๋๋ค. ์ฅ๋ฒฝ์ ์ปดํ์ผ๋ฌ์๊ฒ ๋ฃจํ ๋ณธ๋ฌธ์ด ์ค์ํ ํจ๊ณผ๊ฐ ์๋ค๊ณ ๊ฐ์ ํ๋๋ก ๊ฐ์ ํฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก, ์ต์ ํ ์ฅ๋ฒฝ์ ๋ฉ๋ชจ๋ฆฌ ์ฝ๊ธฐ ๋๋ ์ฐ๊ธฐ์ ์์๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํด ์ฌ์ฉ๋ ์๋ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ํ์ด๋จธ ์ธํฐ๋ฝํธ๊ฐ ๋ฐ์ํ ๋๋ง๋ค ๊ธ๋ก๋ฒ ๋ณ์ timer_put_char์ ์ ์ฅ๋ ๋ฌธ์๋ฅผ ์ฝ์์ ์ถ๋ ฅํ๋ "๊ธฐ๋ฅ"์ ์ถ๊ฐํ๋ค๊ณ ๊ฐ์ ํด๋ณด๊ฒ ์ต๋๋ค. ๋จ, ๊ธ๋ก๋ฒ ๋ถ์ธ ๋ณ์ timer_do_put์ด true์ธ ๊ฒฝ์ฐ์๋ง ์ถ๋ ฅํด์ผ ํฉ๋๋ค. ์ด ๊ฒฝ์ฐ x๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ํด ์ต์ ํ ์ฅ๋ฒฝ์ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข์ต๋๋ค.
timer_put_char = 'x';
barrier ();
timer_do_put = true;
์ฅ๋ฒฝ์ด ์์ผ๋ฉด ์ปดํ์ผ๋ฌ๋ ์ฐ์ฐ์ ์ฌ๋ฐฐ์ดํ ์์ ๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ฝ๋์ ๋ฒ๊ทธ๊ฐ ์์ ์ ์์ต๋๋ค. ์ปดํ์ผ๋ฌ๋ ์ฐ์ฐ์ ์์๋ฅผ ์ ์งํ ์ด์ ๊ฐ ์๋ค๊ณ ํ๋จํ๋ฉด ์ฐ์ฐ์ ์์๋ฅผ ๋ฐ๊ฟ ์ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ, ์ปดํ์ผ๋ฌ๋ ํ ๋น๋ฌธ์ ์์๊ฐ ์ค์ํ๋ค๋ ๊ฒ์ ์์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ์ต์ ํ ๊ธฐ๋ฅ์ด ํ ๋น๋ฌธ์ ์์๋ฅผ ๋ฐ๊ฟ ์ ์์ต๋๋ค. ์ค์ ๋ก ์ด๋ ๊ฒ ํ ์ง๋ ์ ์ ์์ผ๋ฉฐ, ์ปดํ์ผ๋ฌ์๊ฒ ๋ค๋ฅธ ์ต์ ํ ํ๋๊ทธ๋ฅผ ์ ๋ฌํ๊ฑฐ๋ ๋ค๋ฅธ ๋ฒ์ ์ ์ปดํ์ผ๋ฌ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ค๋ฅธ ๋์์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
๋ค๋ฅธ ํด๊ฒฐ์ฑ ์ ํ ๋น๋ฌธ ์ฃผ๋ณ์์ ์ธํฐ๋ฝํธ๋ฅผ ๋นํ์ฑํํ๋ ๊ฒ์ ๋๋ค. ์ด๋ ์ฌ๋ฐฐ์ด์ ๋ฐฉ์งํ์ง๋ ์์ง๋ง, ์ธํฐ๋ฝํธ ํธ๋ค๋ฌ๊ฐ ํ ๋น๋ฌธ ์ฌ์ด์ ๊ฐ์ ํ๋ ๊ฒ์ ๋ฐฉ์งํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ธํฐ๋ฝํธ๋ฅผ ๋นํ์ฑํํ๊ณ ๋ค์ ํ์ฑํํ๋ ๋ฐํ์ ๋น์ฉ์ด ์ถ๊ฐ๋ก ๋ฐ์ํฉ๋๋ค.
enum intr_level old_level = intr_disable ();
timer_put_char = 'x';
timer_do_put = true;
intr_set_level (old_level);
๋ ๋ฒ์งธ ํด๊ฒฐ์ฑ ์ timer_put_char์ timer_do_put์ ์ ์ธ์ volatile๋ก ํ์ํ๋ ๊ฒ์ ๋๋ค. ์ด ํค์๋๋ ์ปดํ์ผ๋ฌ์๊ฒ ๋ณ์๊ฐ ์ธ๋ถ์์ ๊ด์ฐฐ ๊ฐ๋ฅํ๋ฉฐ ์ต์ ํ์ ์์ ๋๋ฅผ ์ ํํ๋ค๊ณ ์๋ ค์ค๋๋ค. ๊ทธ๋ฌ๋ volatile์ ์๋ฏธ๋ ๋ช ํํ๊ฒ ์ ์๋์ด ์์ง ์์ผ๋ฏ๋ก ์ผ๋ฐ์ ์ธ ํด๊ฒฐ์ฑ ์ผ๋ก ์ฌ์ฉํ๊ธฐ์๋ ์ ์ ํ์ง ์์ต๋๋ค. ๊ธฐ๋ณธ Pintos ์ฝ๋๋ ์ ํ volatile์ ์ฌ์ฉํ์ง ์์ต๋๋ค.
๋ค์์ ํด๊ฒฐ์ฑ ์ด ์๋๋๋ค. ์๋ํ๋ฉด ๋ฝ์ ์ธํฐ๋ฝํธ๋ฅผ ๋ฐฉ์งํ์ง ์์ผ๋ฉฐ, ๋ฝ์ด ๋ณด์ ๋ ์์ญ ๋ด์์ ์ฝ๋์ ์ฌ๋ฐฐ์ด์ ์ปดํ์ผ๋ฌ๋ก๋ถํฐ ๋ฐฉ์งํ์ง ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
lock_acquire (&timer_lock); /* INCORRECT CODE */
timer_put_char = 'x';
timer_do_put = true;
lock_release (&timer_lock);
์ปดํ์ผ๋ฌ๋ ์ธ๋ถ์์, ์ฆ ๋ค๋ฅธ ์์ค ํ์ผ์์ ์ ์๋ ํจ์์ ํธ์ถ์ ๋ํด์๋ ์ต์์ ์ต์ ํ ์ฅ๋ฒฝ์ผ๋ก์ ๋ํฉ๋๋ค. ๊ตฌ์ฒด์ ์ผ๋ก ๋งํด๋ณด๋ฉด, ์ปดํ์ผ๋ฌ๋ ์ธ๋ถ์์ ์ ์๋ ํจ์๋ค์ด ์ ์ , ๋์ ์ผ๋ก ํ ๋น๋ ์ด๋ค ๋ฐ์ดํฐ๋ ์ฃผ์๊ฐ ์ ํด์ง ์ด๋ค ์ง์ญ๋ณ์๋ค์๋ ์ ๊ทผ ํ ์ ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค. ์ด๊ฑด ๋ช ์์ ์ธ ์ฅ๋ฒฝ์ด ์๋ต๋ ์ ์๋ค๋ ๋ป์ ๋๋ค. Pintos๊ฐ ๋ช ์์ ์ธ ์ฅ๋ฒฝ์ ๊ฑฐ์ ๊ฐ๊ณ ์์ง ์๋ ์ด์ ์ ๋๋ค.
๊ฐ์ ์์ค ํ์ผ์์์ ์ ์๋ ํจ์๋ ํฌํจ๋ ํค๋๋ฅผ ์ต์ ํ ์ฅ๋ฒฝ์ผ๋ก ๊ธฐ๋ํ๋ฉด ์๋ฉ๋๋ค. ์ต์ ํ ์ฅ๋ฒฝ์ ์ฌ์ง์ด ํจ์๋ฅผ ์ ์ํ๊ธฐ ์ ์๋, ํด๋น ํจ์์ ์ฌ์ฉ์ ๋ํด ์ ์ฉ๋ ์ ์์ต๋๋ค. ์ด์ ๋ ์ปดํ์ผ๋ฌ๋ ์ต์ ํ๋ฅผ ์ํํ๊ธฐ ์ ์ ๋จผ์ ์ ์ฒด ์์ค ํ์ผ์ ์ฝ๊ณ ํ์ฑํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
thread๊ฐ priority์ ๋ฐ๋ผ ์คํ์ด ๋๋๋ก ๊ตฌํํ๋ ค๋ฉด '์ง์'๋ฅผ ๋ง๋ค์ด์ผํ๋ค. pintOS์์๋ ์ด๋ฅผ lock&semaphore๋ก ๊ตฌํํ๋ค. ์์ gitbook์ ๋์์๋ฏ์ด pintOS์์๋ lock๋ฅผ semaphore 1๋ก ๊ตฌํํ๋ค. ํ์ง๋ง lock๊ณผ semaphore์ ์ฐจ์ด์ ์ lock์ ๊ฐ์ง๊ณ ์๋ thread๊ฐ lock์ ๋์์ค ์ ์๋ค๋ ๊ฒ์ด๋ค.
//thread/synch.c
void sema_init(struct semaphore *sema, unsigned value)
{
ASSERT(sema != NULL);
sema->value = value;
list_init(&sema->waiters);
}
assert๋ฅผ ํตํด ์ธ๋งํฌ์ด๊ฐ null์ด ์๋์ง ํ์ธํ๊ณ , ์ธ๋งํฌ์ด ๊ตฌ์กฐ์ฒด์ value์ ์ฃผ์ด์ง ์ธ์ ๊ฐ์ ์ด๊ธฐ๊ฐ์ผ๋ก ํ ๋นํ๋ค.
์ดํ ์ธ๋งํฌ์ด ๊ตฌ์กฐ์ฒด ๋ด๋ถ์ waiters๋ฆฌ์คํธ๋ฅผ list_init()ํจ์๋ฅผ ์ฌ์ฉํด ์ด๊ธฐํ ํ๋ค.
๊ธฐ์กด์ ์๋ sema_down()์ ์๋์ ๊ฐ๋ค.
//thread/synch.c
/* Down or "P" operation on a semaphore. Waits for SEMA's value
to become positive and then atomically decrements it.
This function may sleep, so it must not be called within an
interrupt handler. This function may be called with
interrupts disabled, but if it sleeps then the next scheduled
thread will probably turn interrupts back on. This is
sema_down function. */
void
sema_down (struct semaphore *sema) {
enum intr_level old_level;
ASSERT (sema != NULL);
ASSERT (!intr_context ());
old_level = intr_disable ();
while (sema->value == 0) {
list_push_back (&sema->waiters, &thread_current ()->elem);
thread_block ();
}
sema->value--;
intr_set_level (old_level);
}
intr_disable ํจ์๋ฅผ ํธ์ถํ์ฌ ํ์ฌ ์ธํฐ๋ฝํธ ๋ ๋ฒจ์ ์ ์ฅํ๊ณ ์ธํฐ๋ฝํธ๋ฅผ ๋นํ์ฑํ ๋ค, while ๋ฃจํ๋ฅผ ์ฌ์ฉํ์ฌ ์ธ๋งํฌ์ด์ ๊ฐ์ด 0์ธ ๋์ ๋๊ธฐํ๋ค.
while ๋ฃจํ๋ฅผ ๋น ์ ธ๋์ค๋ฉด ์ธ๋งํฌ์ด์ ๊ฐ์ 1 ๊ฐ์์ํจ๋ค. ์ดํ intr_set_level ํจ์๋ฅผ ํธ์ถํ์ฌ ์ด์ ์ธํฐ๋ฝํธ ๋ ๋ฒจ์ ๋ณต์ํ๊ณ ์ธํฐ๋ฝํธ๋ฅผ ๋ค์ ํ์ฑํํ๋ค.
์ด๋ง์ ์ ๋ฆฌํด๋ณด๋ฉด, threadA๋ ์ธ๋งํฌ์ด๊ฐ 0์ธ ๋์ ๋ธ๋ก๋๊ณ ๋๊ธฐ์ํ์ ์๋ค๊ฐ running์ํ์ ์๋ ๋ค๋ฅธ thread๊ฐ ํด๋น ์ธ๋งํฌ์ด๋ฅผ 1๋ก ์ฌ๋ ค์ฃผ๋ฉด ๊ธฐ๋ค๋ฆฌ๊ณ ์๋ threadA๊ฐ while๋ฌธ์ ๋น ์ ธ๋์ค๊ณ threadA๊ฐ ์ธ๋งํฌ์ด๋ฅผ ๊ฐ์ ธ๊ฐ๋ค๋ ๋ป์ผ๋ก 0์ผ๋ก(sema->value--;) ๋ง๋ค์ด์ฃผ๋ ๊ฒ์ด๋ค. ์ด๋ฅผ ํตํด ๋ค๋ฅธ ์ค๋ ๋๊ฐ ๋์์ ์ ๊ทผํ์ง ๋ชปํ๋๋ก ํ๋ค.
//thread/synch.c
void sema_down(struct semaphore *sema)
{
enum intr_level old_level;
ASSERT(sema != NULL);
ASSERT(!intr_context());
old_level = intr_disable();
while (sema->value == 0) /* sema์ ์ ๊ทผํ ์ ์์ ๋ */
{
list_insert_ordered(&sema->waiters, &thread_current()->elem, &priority_less, NULL); /* ์ ๊ทผ ๊ถํ์ด ์๊ธฐ๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ค๋ ๋๋ฅผ waiters์ ์ถ๊ฐ */
thread_block(); /* ํด๋น ์ค๋ ๋ block */
}
sema->value--;
intr_set_level(old_level);
}
waiters list์ ๋ค์ด๊ฐ ๋, priority ์์ผ๋ก ์ ๋ ฌ๋์ด ๋ฃ์ด์ ธ์ priority์ ๋ฐ๋ผ ready_list๊ฐ ์ ๋ ฌ๋ ์ ์๋๋ก ์์ ํ๋ค.
๊ธฐ์กด์ ์๋ sema_up()์ ์๋์ ๊ฐ๋ค.
//thread/synch.c
/* Up or "V" operation on a semaphore. Increments SEMA's value
and wakes up one thread of those waiting for SEMA, if any.
This function may be called from an interrupt handler. */
void
sema_up (struct semaphore *sema) {
enum intr_level old_level;
ASSERT (sema != NULL);
old_level = intr_disable ();
if (!list_empty (&sema->waiters))
thread_unblock (list_entry (list_pop_front (&sema->waiters),
struct thread, elem));
sema->value++;
intr_set_level (old_level);
}
์ธํฐ๋ฝํธ ๋นํ์ฑํ๋ฅผ ํตํด ํ์ฌ ์คํ ์ค์ธ ์ฝ๋๊ฐ ์ค๋จ๋์ง ์๋๋ก ํ๋ค. ์ธ๋งํฌ์ด์ ๋๊ธฐ ๋ฆฌ์คํธ์ ๋๊ธฐ ์ค์ธ ์ค๋ ๋๊ฐ ์๋์ง ํ์ธํ๊ณ , ๋๊ธฐ์ค์ธ ์ค๋ ๋๊ฐ ์๋ค๋ฉด list_pop_front ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ฒซ ๋ฒ์งธ ์ค๋ ๋๋ฅผ ๊บผ๋ด๊ณ , ์ด ์ค๋ ๋๋ฅผ thread_unblock ํจ์๋ฅผ ํธ์ถํ์ฌ ์ค๋น ์ํ๋ก ๋ณ๊ฒฝํ๋ค. ์ด๋ฅผ ํตํด ์ธ๋งํฌ์ด๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ณ ์๋ ์ค๋ ๋๊ฐ ์คํ ๊ฐ๋ฅํ ์ํ๋ก ์ ํํ๋ค.
์ธ๋งํฌ์ด์ ๊ฐ์ 1 ์ฆ๊ฐ์ํด์ผ๋ก์จ ํ์ฌ running์ํ์ ์๋ thread๋ ํด๋น ์ธ๋งํฌ์ด๊ฐ ์งํค๊ณ ์๋ ์์์ ๋ค์ผ์์ ์๋ฆฌ๊ณ , waiters list์ head์ ์๋ thread๊ฐ ํด๋น์์์ ์ ๊ทผ ๊ฐ๋ฅํ๋๋ก ํ๋ค.
๋ง์ง๋ง์ผ๋ก, ์ด์ ์ ์ธํฐ๋ฝํธ ๋ ๋ฒจ์ ๋ณต์ํ๊ณ ์ธํฐ๋ฝํธ๋ฅผ ๋ค์ ํ์ฑํํ๋ค.
//thread/synch.c
void sema_up(struct semaphore *sema)
{
enum intr_level old_level;
ASSERT(sema != NULL);
old_level = intr_disable();
if (!list_empty(&sema->waiters))
{
list_sort(&sema->waiters, &priority_less, NULL); /* ์ฐ์ ์์๊ฐ ๋ณ๊ฒฝ๋์ ๊ฒฝ์ฐ waiters ์ ๋ ฌ */
thread_unblock(list_entry(list_pop_front(&sema->waiters),
struct thread, elem)); /* prioirty๊ฐ ๊ฐ์ฅ ๋์ ์ค๋ ๋๋ฅผ unblock */
}
sema->value++;
test_max_priority();
intr_set_level(old_level);
}
waiters๋ฆฌ์คํธ์ ์๋ head๋ฅผ ๋ฝ์์ฌ๋ priority์ ๋ฐ๋ผ ํ๋ฒ ๋ ์ ๋ ฌ์ํค๊ณ ๋ฝ์์ด์ผ๋ก์จ priority์ ๋ฐ๋ผ ๊ตฌํ๋ ์ ์๋๋ก ์์ ํ๋ค.
์ดํ, ํ์ฌ ์คํ ์ค์ธ ์ค๋ ๋์ ์ฐ์ ์์๋ณด๋ค ๋์ ์ฐ์ ์์๋ฅผ ๊ฐ์ง ์ค๋ ๋๊ฐ ์ค๋น ๋ฆฌ์คํธ์ ์๋ ๊ฒฝ์ฐ์๋ง ํ์ฌ ์ค๋ ๋๋ฅผ ์๋ณดํ๋test_max_priority()ํจ์๋ฅผ ์ถ๊ฐํจ์ผ๋ก์จ prioirty๊ฐ ๊ฐ์ฅ ๋์ ์ค๋ ๋๊ฐ running thread๊ฐ ๋๋๋ก ๊ตฌํํ๋ค.
//threads/thread.c
/* Atomically releases LOCK and waits for COND to be signaled by
some other piece of code. After COND is signaled, LOCK is
reacquired before returning. LOCK must be held before calling
this function.
The monitor implemented by this function is "Mesa" style, not
"Hoare" style, that is, sending and receiving a signal are not
an atomic operation. Thus, typically the caller must recheck
the condition after the wait completes and, if necessary, wait
again.
A given condition variable is associated with only a single
lock, but one lock may be associated with any number of
condition variables. That is, there is a one-to-many mapping
from locks to condition variables.
This function may sleep, so it must not be called within an
interrupt handler. This function may be called with
interrupts disabled, but interrupts will be turned back on if
we need to sleep. */
cont_wait์ ๋ฌ๋ ค์๋ ์ฃผ์์ ํด์ํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
LOCK์ ์์์ ์ผ๋ก ํด์ ํ๊ณ COND๊ฐ ๋ค๋ฅธ ์ฝ๋์ ์ํด ์๊ทธ๋๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฝ๋๋ค. COND๊ฐ ์๊ทธ๋๋๋ฉด ๋ฐํํ๊ธฐ ์ ์ LOCK์ ๋ค์ ํ๋ํฉ๋๋ค. ์ด ํจ์๋ฅผ ํธ์ถํ๊ธฐ ์ ์ LOCK์ ๋ณด์ ํด์ผ ํฉ๋๋ค.
์ด ํจ์๋ก ๊ตฌํ๋ ๋ชจ๋ํฐ๋ "Mesa" ์คํ์ผ ์ด๋ฉฐ "Hoare" ์คํ์ผ์ด ์๋๋๋ค. ๋ฐ๋ผ์ ๋ณดํต ํธ์ถ์๋ ๋๊ธฐ๊ฐ ์๋ฃ๋ ํ ์กฐ๊ฑด์ ๋ค์ ํ์ธํ๊ณ ํ์ํ ๊ฒฝ์ฐ ๋ค์ ๋๊ธฐํด์ผ ํฉ๋๋ค.
ํน์ ์กฐ๊ฑด ๋ณ์๋ ํ๋์ ๋ฝ์๋ง ์ฐ๊ฒฐ๋์ง๋ง, ํ๋์ ๋ฝ์ ์ฌ๋ฌ ์กฐ๊ฑด ๋ณ์์ ์ฐ๊ฒฐ๋ ์ ์์ต๋๋ค. ์ฆ, ๋ฝ์์ ์กฐ๊ฑด ๋ณ์๋ก์ ์ผ๋๋ค ๋งคํ์ด ์์ต๋๋ค.
์ด ํจ์๋ sleep์ด ๋ฐ์ํ ์ ์์ผ๋ฏ๋ก ์ธํฐ๋ฝํธ ํธ๋ค๋ฌ ๋ด์์ ํธ์ถํด์๋ ์๋ฉ๋๋ค. ์ธํฐ๋ฝํธ๊ฐ ๋นํ์ฑํ๋ ์ํ์์ ํธ์ถ๋๋๋ผ๋, sleep์ด ํ์ํ ๊ฒฝ์ฐ ์ธํฐ๋ฝํธ๊ฐ ๋ค์ ํ์ฑํ๋ฉ๋๋ค.
์ฃผ์์์ ์ธ๊ธ๋ mesa ์คํ์ผ๊ณผ hoare์คํ์ผ์ ๋ํด ์์๋ณด์.
Mesa ์คํ์ผ:
- ์ ํธ(signal)์ ๋๊ธฐ(wait) ์ฐ์ฐ์ด ์์์ ์ด์ง ์์ต๋๋ค. ์ฆ, ๋๊ธฐ ์ค์ธ ์ค๋ ๋๊ฐ ์ ํธ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ๋์ ์ ํธ๋ฅผ ๋ณด๋ธ ์ค๋ ๋๊ฐ ๋๊ธฐ ์ค์ธ ์ค๋ ๋๋ฅผ ๊นจ์ฐ์ง ์์ ์ ์์ต๋๋ค.
- ๋๊ธฐ ์ค์ธ ์ค๋ ๋๊ฐ ๊นจ์ด๋ ํ์๋ ๋ค์ ์กฐ๊ฑด์ ํ์ธํด์ผ ํฉ๋๋ค. ๋ง์ฝ ์ฌ์ ํ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋์ง ์์๋ค๋ฉด, ๋ค์ ๋๊ธฐํด์ผ ํ ์ ์์ต๋๋ค.
Hoare ์คํ์ผ:
- ์ ํธ(signal)์ ๋๊ธฐ(wait) ์ฐ์ฐ์ด ์์์ ์ ๋๋ค. ์ฆ, ์ ํธ๋ฅผ ๋ณด๋ธ ์ค๋ ๋๋ ๋๊ธฐ ์ค์ธ ์ค๋ ๋๋ฅผ ๊นจ์ฐ๊ณ , ๋๊ธฐ ์ค์ธ ์ค๋ ๋๋ ์ ํธ๋ฅผ ๋ฐ์ ๋๊น์ง ๋๊ธฐํฉ๋๋ค.
- ๋๊ธฐ ์ค์ธ ์ค๋ ๋๊ฐ ๊นจ์ด๋ ํ์๋ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋์์์ ๊ฐ์ ํ ์ ์์ต๋๋ค. ๋ฐ๋ผ์ ์กฐ๊ฑด์ ํ์ธํ ํ์ ์์ด ์งํํ ์ ์์ต๋๋ค.
๋๊ธฐ ์ค์ธ ์ค๋ ๋๊ฐ ๊นจ์ด๋ ํ์๋ ๋ค์ ์กฐ๊ฑด์ ํ์ธํด์ ํ๋ ๊ฒ์ ์ผ๋ํด๋๊ณ , ์ด๋ฒ์๋ ๊ธฐ์กด ์ฝ๋์ ๋ํด ์์๋ณด์.
//thread/synch.c
void
cond_wait (struct condition *cond, struct lock *lock) {
struct semaphore_elem waiter;
ASSERT (cond != NULL);
ASSERT (lock != NULL);
ASSERT (!intr_context ());
ASSERT (lock_held_by_current_thread (lock));
sema_init (&waiter.semaphore, 0);
list_push_back (&cond->waiters, &waiter.elem);
lock_release (lock);
sema_down (&waiter.semaphore);
lock_acquire (lock);
}
cond_wait ํจ์๋ ๋ชจ๋ํฐ์์ ์ฝ๋๊ฐ ํน์ ์กฐ๊ฑด์ด ์ฐธ์ด ๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆด ๋, ๊ทธ ์ฝ๋๋ ์ํ๋ ์กฐ๊ฑด๊ณผ ๊ด๋ จ๋ ์ปจ๋์ ๋ณ์๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ํจ์๋ค.(gitbook์ฐธ๊ณ )
gtibook์์๋ "์์ฐ์" ์ค๋ ๋๊ฐ ๋ฌธ์๋ฅผ ์์ฑํ๊ณ ํ๋ ์ด์์ "์๋น์" ์ค๋ ๋๊ฐ ๋ฌธ์๋ฅผ ์ฝ๋ ๋ฒํผ ์ฒ๋ฆฌ๋ฅผ ์์๋ก ๋ค๊ณ ์๋๋ฐ, ํ '์์ฐ์'์ค๋ ๋๋ ํด๋น list(waiters)๊ฐ ๋น์์ผ๋ฉด buffer๋ฅผ ๊ณ์ ์๊ณ , '์๋น์' ์ค๋ ๋๋ ๋ฒํผ๊ฐ list(waiters)์ ์์ด๋ฉด buffer๋ฅผ ๋น์ด๋ด๋๋ก ํ๋ ๋ฐฉ๋ฒ์ด๋ค.
struct condition
{
struct list waiters; /* List of waiting threads. */
};
struct condtion์ ๋ณด๋ฉด ํด๋น list๋ฅผ ์ ์ธํ๋๋ก ๋์ด์๋ค. ๊ธฐ์กด ์ฝ๋์์๋ ๋๊ธฐ ์ค๋ ๋์ semaphore_elem์ด waiters ๋ฆฌ์คํธ์ ๋ค์ชฝ์ ์ถ๊ฐ๋๋ ๋ฐฉ์์ด๋ค. ์ด๋ฅผ ์ฐ์ ์์๋ก ์ ๋ ฌ๋ ์์น์ ์ฝ์ ๋๋๋ก ๋ณ๊ฒฝํ์. ๊ทธ๋ฆฌ๊ณ ๋๊ธฐ ์ค๋ ๋์ semaphore_elem์ ์ค๋ ๋์ ์ฐ์ ์์๋ฅผ ์ ์ฅํ๋๋ก ์ถ๊ฐํ์.
//thread/synch.c
void cond_wait(struct condition *cond, struct lock *lock)
{
struct semaphore_elem waiter;
ASSERT(cond != NULL);
ASSERT(lock != NULL);
ASSERT(!intr_context());
ASSERT(lock_held_by_current_thread(lock));
sema_init(&waiter.semaphore, 0);
list_insert_ordered(&cond->waiters, &waiter.elem, &sem_priority_less, NULL);
lock_release(lock);
sema_down(&waiter.semaphore);
lock_acquire(lock);
}
๊ธฐ์กด ์ฝ๋์์๋ ๋๊ธฐ ์ค๋ ๋๋ค์ด ์์๋๋ก waiters ๋ฆฌ์คํธ์ ์ถ๊ฐ๋๊ณ ๊นจ์ด๋๋ค.
์์ ํ ์ฝ๋์์๋ ๋๊ธฐ ์ค๋ ๋๋ค์ด ์ฐ์ ์์ ์์ผ๋ก ์ ๋ ฌ๋ ์์น์ ์ฝ์
๋๊ณ ๊นจ์ด๋๋ค. ๋ํ ๋๊ธฐ ์ค๋ ๋์ ์ฐ์ ์์๋ฅผ ์ ์ฅํ์ฌ ์ ๋ ฌ ๊ธฐ์ค์ผ๋ก ํ์ฉ๋๋ค.
์๋๋ cond_signal()์ ๊ธฐ๋ณธ์ฝ๋๋ค.
//threads/synch.c
/* If any threads are waiting on COND (protected by LOCK), then
this function signals one of them to wake up from its wait.
LOCK must be held before calling this function.
An interrupt handler cannot acquire a lock, so it does not
make sense to try to signal a condition variable within an
interrupt handler. */
void
cond_signal (struct condition *cond, struct lock *lock UNUSED) {
ASSERT (cond != NULL);
ASSERT (lock != NULL);
ASSERT (!intr_context ());
ASSERT (lock_held_by_current_thread (lock));
if (!list_empty (&cond->waiters))
sema_up (&list_entry (list_pop_front (&cond->waiters),
struct semaphore_elem, elem)->semaphore);
}
cond_signal ํจ์๋ ์กฐ๊ฑด ๋ณ์ cond์ ๋ฝ lock์ ์ธ์๋ก ๋ฐ์์์ ์ฌ์ฉํ๊ณ , ๋๊ธฐ ์ค์ธ ์ค๋ ๋ ์ค ํ๋๋ฅผ ๊นจ์ด๋ค. ์ด๋ฅผ condition variable์waiters list๋ฅผ์ฐ์ ์์๋ก ์ฌ์ ๋ ฌ ํ๋๋ก ์์ ํ์.
//threads/synch.c
void cond_signal(struct condition *cond, struct lock *lock UNUSED)
{
ASSERT(cond != NULL);
ASSERT(lock != NULL);
ASSERT(!intr_context());
ASSERT(lock_held_by_current_thread(lock));
if (!list_empty(&cond->waiters))
{
list_sort(&cond->waiters, sem_priority_less, NULL);
sema_up(&list_entry(list_pop_front(&cond->waiters),
struct semaphore_elem, elem)
->semaphore);
}
}
sema_up()์ ํ๊ธฐ ์ list_sort ํจ์๋ฅผ ์ฌ์ฉํ์ฌ waiters ๋ฆฌ์คํธ๋ฅผ sem_priority_less ๋น๊ต ํจ์๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌํ๋๋ก ํ์. sem_priority_less()๋ ์์ง ๊ตฌํ์ ์ํด์คฌ์ง๋ง ์ด์ ์ priority_less()ํจ์์ ๊ตฌ์กฐ๊ฐ ๋น์ทํ๋ค.
//include/threads/synch.h
bool sem_priority_less(const struct list_elem *a, const struct list_elem *b, void *aux);
//threads/synch.c
bool sem_priority_less(const struct list_elem *a, const struct list_elem *b, void *aux){
struct semaphore_elem *sa = list_entry(a, struct semaphore_elem, elem);
struct semaphore_elem *sb = list_entry(b, struct semaphore_elem, elem);
struct thread *thread_a = list_entry(list_begin(&sa->semaphore.waiters), struct thread, elem);
struct thread *thread_b = list_entry(list_begin(&sb->semaphore.waiters), struct thread, elem);
return thread_a->priority > thread_b->priority;
}
sem_priority_less() ์ธ์๋ก ๋ค์ด์ค๋ ๋ ๋ ๊ฐ์ semaphore_elem ๊ตฌ์กฐ์ฒด๋ฅผ list_entry()ํจ์๋ฅผ ํ๊ณ ์ฌ๋ผ๊ฐ thread ๊ตฌ์กฐ์ฒด ์์ ์๋ priority๊ฐ์ผ๋ก ๋น๊ตํ์ฌ ์ฐ์ ์์ ์์๋ฅผ ๊ฒฐ์ ํ๋ ๋น๊ต ํจ์๋ค.
์ด๋ฌํ ๋ฐฉ์์ผ๋ก sem_priority_less ํจ์๋ waiters ๋ฆฌ์คํธ์์ semaphore_elem์ ์ฐ์ ์์์ ๋ฐ๋ผ ์ ๋ ฌํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
๊ธธ๊ณ ๋ ๊ธด synch ๋ด์ฉ์ ๋ฆฌ๊ฐ ๋๋ฌ๋ค. ์ฌ๊ธฐ๊น์ง ์ ์์ ์ผ๋ก ๊ตฌํ์ด ๋์๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ 14๊ฐ์ fail์ ๋ณผ ์ ์๋ค. ๐ฎโ๐จ