1. Classic Problems of Synchronization
- The bounded-buffer problem
- The readers-writers problem
- The dining-philosophers problem
1) The Bounded-Buffer Problem
๋ ์ข ๋ฅ์ ํ๋ก์ธ์ค๊ฐ ์๋ค. ํ๋๋ Producer, ๋ ๋ค๋ฅธ ํ๋๋ Consumer์ด๋ค. ์ด๋ ์์ฐ์-์๋น์ ๋ฌธ์ (Producer-Consumer Problem)์ด๋ผ๊ณ ๋ ๋ถ๋ฆฐ๋ค. ์ฌ๊ธฐ์ ๋ฐ์ํ ์ ์๋ ๋ฌธ์ ์ ์ ์ด๋ producer๊ฐ ๋ฒํผ ์ค ๋น ๊ณณ์ ๋ฐ์ดํฐ๋ฅผ ์ฐ๋ ค๊ณ ํ ๋, interrupt๊ฐ ๋ฐ์ํ์ฌ ๋ค๋ฅธ ํ๋ก์ธ์คํํ ๊ณต์ ์์์ด ๋์ด๊ฐ์ ๋ค๋ฅธ ํ๋ก์ธ์ค๊ฐ ํด๋นํ๋ ๋น ๊ณณ์ ๋ฐ์ดํฐ๋ฅผ ์ธ ๋ ๋ํ๋ ์ ์๋ค. ๊ทธ๋ ๊ฒ ๋๋ฉด ๋ ์ค ํ ํ๋ก์ธ์ค์ ๋ฐ์ดํฐ๊ฐ ์ ์ค๋ ๊ฐ๋ฅ์ฑ์ด ์๋ค๋ ๊ฒ์ด๋ค.
- ๋ง์ฝ buffer๊ฐ ๊ฝ ์ฐผ๋ค๋ฉด producer๋ consumer๊ฐ item์ ์ญ์ ํ ๋๊น์ง ๊ธฐ๋ค๋ ค์ผ ํ๋ค.
- producer๋ buffer ์์ empty space๊ฐ ํ์ํ๋ค.
- # of empty slot ์ semaphore empty์ ์ํด ๋ํ๋์ง๋ค.
- ๋ง์ฝ buffer๊ฐ ๋น์๋ค๋ฉด consumer๋ producer๊ฐ item์ ๋ฃ์ ๋๊น์ง ๊ธฐ๋ค๋ ค์ผ ํ๋ค.
- consumer๋ buffer ์์ item์ด ํ์ํ๋ค.
- # of item์ semaphore full์ ์ํด์ ๋ํ๋์ง๋ค.
- Producer-consumer problem with bounded buffer
- Semaphores: full = 0, empty = n, mutex = 1;
- ๋ฒํผ์ ๊ฐ์ n, binary semaphore : mutex, counting semaphore: empty, full
//Producer
while(true){
...
/* produce an item in next_produced(์๋ก์ด item ํ๋ ์์ฑ) */
...
wait(empty) // empty ๋ empty-1์ด ๋๋ค. empty๊ฐ 1~n ๊ฐ์ค์์ ์์ํ๋ค๋ฉด ๋ค์์ค๋ก ๋์ด๊ฐ.
//๋ง์ฝ empty๊ฐ 0์ด์๋ค๋ฉด -1์ด ๋์ด block๋
wait(mutex)
...
/* add next produced to the buffer(๋ฒํผ์ ์๋ก ์์ฑ๋ item์ ์ถ๊ฐ) */
...
signal(mutex); //๋ฒํผ์ item์ ์ถ๊ฐํ์ผ๋ฉด,
//binary semaphore์ signal ํจ์๋ฅผ ํธ์ถํ์ฌ critical section์ ํ์ถ์ ์๋ฆฐ๋ค.
signal(full); //signal(full)์ ํธ์ถํด์ 0์ ์ด๊ธฐ๊ฐ์ count++๋ฅผ ํตํด 1๋ก ๋ง๋ค์ด์ค๋ค.
}
//Consumer
while(true){
wait(full); // ๋ฐ์ดํฐ๋ฅผ ๊บผ๋ด๊ธฐ ์ํด full ์์์ ์ป๋๋ค.
//๋ฒํผ์ item์ด ํ ๋ฒ๋ ์๋ค์ด๊ฐ๋ค๋ฉด 0์ -1์ด ๋์ด block์ ๊ฑธ๋ฆฐ๋ค.
wait(mutex); //full ์์์ด ์๋ค๋ฉด lock์ ๊ฑธ๊ธฐ ์ํด mutex๋ฅผ ์คํํ๋ค.
...
/* remove an item from buffer to next_consumed(ํด๋น ๋ฒํผ์์ item์ ๊บผ๋) */
...
signal(mutex); //item์ ๋นผ์จ ์ดํ signal ํจ์๋ฅผ ํธ์ถํ์ฌ critical section ํ์ถ์ ์๋ฆฐ๋ค.
signal(empty); //signal(empty)๋ฅผ ํธ์ถํด ๋ฐ์ดํฐ๊ฐ ์ฑ์์ ธ ์๋ ๋ฒํผ์ ๊ณต๊ฐ ๊ฐ์ ํ๋๋ฅผ ๋บ๋ค.
...
/* consume the item in next consumed(item์ ์๋นํจ) */
...
}
2) The Readers-Writers Problem
Readers์ Writers๋ ํ๋์ ๊ณต์ ํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๊ฐ์ง๊ณ ์๋ค. Readers๋ ๋์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ ์ ์์ง๋ง, ํ writer๊ฐ shared data์ ์ ๊ทผํ ๋, ์ด๋ค thread๋ ๊ทธ๊ฒ์ ์ ๊ทผํ ์ ์๋ค.
- Behavior of a writer
- ๋ง์ฝ ํ thread๊ฐ critical section์ ์์๋, ๋ชจ๋ writers์ ๋ฌด์กฐ๊ฑด ๊ธฐ๋ค๋ ค์ผ ํ๋ค.
- writer์ ์ค์ง ์ด๋ค thread๋ critical section์ ์์ง ์์ ๋์๋ง ์ง์
ํ ์ ์๋ค.
- ๋ชจ๋ thread๋ค์ด critical section์ ์ง์ ํ๋ ๊ฒ์ผ๋ก๋ถํฐ ์ด๋ฅผ ์๋ฐฉํด์ผ ํ๋ค.
- Behavior of a reader
- ๋ง์ฝ ์ด๋ค writer๋ critical section์ ์์ง ์์ผ๋ฉด, ๊ทธ reader๋ critical section์ ๋ค์ด๊ฐ ์ ์๋ค.
- ๋ง์ฝ ์ด๋ค writer๊ฐ critical section์ ์๋ค๋ฉด, reader๋ ๊ทธ writer๊ฐ critical section์ ๋น ์ ธ ๋์ฌ ๋๊น์ง ๊ธฐ๋ค๋ ค์ผ ํ๋ค.
- reader๊ฐ critical section์ ์์ ๋, ์๋ฌด reader๋ critical section์ ์ง์
ํ ์ ์๋ค. ํ์ง๋ง writer์ ๋ถ๊ฐ๋ฅํ๋ค.
- ์ฒซ๋ฒ์งธ reader์ condition์ ๋ค๋ฐ๋ผ์ค๋ reader๋ค๊ณผ ๋ค๋ฅด๋ค.
//Shared data
semaphore mutex = 1;
semaphore rw_mutex = 1;
int read_count = 0;
//Writer
while(true){
//writer์ ๊ณต์ ์์์ ์ ๊ทผํ๋ฉด ๋ค๋ฅธ ๋ชจ๋ ํ๋ก์ธ์ค๋ค์ ์ ๊ทผ์ ์ฐจ๋จํจ.
//binary semaphore๋ก ๋ค๋ฅธ ํ๋ก์ธ์ค๋ค์ critical section ์ง์
์ ์ผ์ฒด ์ฐจ๋จํจ.
wait(rw_mutex);
...
/* writing is performed(writing ์ํ) */
...
signal(rw_mutex);
}
//Reader
while(true){
//Reader์์๋ Writer์ ์ ๊ทผ๋ง ๋ง์ ๋ฟ, Reader๊ฐ ๋ช ๋ช
์ด ์ ๊ทผํ๋์ง๋ ์ ๊ฒฝ์ฐ์ง ์์.
wait(mutex); // read_count์ ๋ํ mutual exclusion์ ๋ณด์ฅํ๊ธฐ ์ํ ์ค์
read_count++; // read_count๊ฐ 0์ด๋ผ๋ฉด 1๋ก ์ฆ๊ฐ์ํจ๋ค.
if(read_count == 1)// read_count๊ฐ 1์ด๋ผ๋ฉด wait(rw_mutex)๋ฅผ ์คํ, ์ด๋ฅผ ํตํด writer์ ์๊ณ๊ตฌ์ญ์ ์ง์
๋ชปํจ.
wait(rw_mutex);//reader๋ read_count๋ฅผ ์ฆ๊ฐ์ํฌ ๋ฟ, wait(rw_mutex)๋ฅผ ํธ์ถํ์ง ์์ผ๋ฏ๋ก ์๊ณ๊ตฌ์ญ์ ๊ทธ๋ฅ ์ง์
.
signal(mutex);
...
/* reading is performed */
...
wait(mutex);
read_count--;
if(read_count == 0)//์๊ณ๊ตฌ์ญ์ ๋จธ๋ฌผ๋ฌ ์๋ Reader๊ฐ ํ๋๋ ์์ ๊ฒฝ์ฐ signal(rw_mutex)๋ฅผ ํธ์ถ
signal(rw_mutex);//๊ทธ๋ผ์ผ๋ก์จ writer๊ฐ ๊ณต์ ์์์ ์ ๊ทผ ๊ฐ๋ฅํ๋ค.
signal(mutex);
}
3) The dining-philosophers problem(์์ฌํ๋ ์ฒ ํ์ ๋ฌธ์ )
'์์ฌํ๋ ์ฒ ํ์ ๋ฌธ์ '๋ 5๋ช ์ ์ฒ ํ์๊ฐ ์์ฌ๋ฅผ ํ๊ณ ์๋ ์ํฉ์ ๊ฐ์ ํ ๋ฌธ์ ์ด๋ค. 5๋ช ์ ์ฒ ํ์๋ค์ด ์ํ์ ํ ์ด๋ธ์ ์์ ์๋๋ฐ ์ฒ ํ์๋ค ์ฌ์ด์๋ ์ ๊ฐ๋ฝ ํ ์ง๋ง ๋์ฌ์๊ณ ์ฒ ํ์ ์์๋ ์์์ด ๋์ฌ์๋ค. ์ฒ ํ์๋ ๋ฐฐ๊ฐ๊ณ ํ๋ฉด ์ ์์ ์๋ ์ ๊ฐ๋ฝ์ ์ฌ์ฉํด์ ์์ฌ๋ฅผ ํ๊ณ ๋ค์ ์ ๊ฐ๋ฝ์ ์์์น์ ๋๊ณ ์๊ฐ์ ํด์ผํ๋ค. ์ด๋ ์๊ธฐ๋ ๋ฌธ์ ๋ ํ๋ช ์ด ์์์ ๋จน๊ณ ์๋ค๋ฉด ์ ์์ ์์ ์ฒ ํ์ ๋๋ช ์ ๋ฐฐ๊ฐ ๊ณ ํ๋ ์์์ ๋จน์ ์ ์๋ ์ํฉ์ด ์จ๋ค. ๋ฐ๋ผ์ ์ ๊ฐ๋ฝ์ ๋ํ ์ ๊ทผ์ ํ๋ ๊ฒ์ ๋ํ ๋ฌธ์ ํด๊ฒฐ์ด ํ์ํ๋ค.
ํด๊ฒฐ์ฑ ์ deadlock-free์ starcation-free๊ฐ ๋์ด์ผ ํ๋ค. ์ฆ, ๊ต์ฐฉ๊ณผ ๊ธฐ์๊ฐ ์๊ธฐ์ง ์๊ฒ ํ๋ ๋ฐฉ๋ฒ์ ์๊ฐํด์ผ ํ๋ค.
- Semaphore ํด๊ฒฐ๋ฐฉ๋ฒ
- Shared data: Bowl of rice(dataset) / Semaphore chopstick[5] initialized to 1
- ์๋ ์ฝ๋๋ ํด๊ฒฐ๊ฐ๋ฅ์ฑ์ด ์๋ ๋ฐฉ๋ฒ์ ๋ํ๋ธ๋ค. ํ์ง๋ง deadlock์ด ๋ฐ์ํ ์ ์๋ค.
while(true){
wait(chopstick[i]); //pick up left chopstick
wait(chopstick[(i+1) % 5]); //pick up right chopstick
/* eat for a while */
signal(chopstick[i]); //release left chopstick
signal(chopstick[(i+1) % 5]); //release right chopstick
/* think for a while */
}
Deadlock์ด ๋ฐ์ํ๋ ๊ฒฝ์ฐ๋ ์ด๋ค ๊ฒฝ์ฐ์ผ๊น?
- 5๋ช ์ ์ฒ ํ์๊ฐ ๋ค๊ฐ์ด ๋ฐฐ๊ณ ํ์ ๋ค๊ฐ์ด ๋์์ ์์ ์ผ์ชฝ์ ์ ๊ฐ๋ฝ์ ์ก๋๋ค๋ฉด chopstick์ ๋ชจ๋ ์์๋ 0์ด ๋๋ค. ๊ฒฐ๊ตญ ์ฒ ํ์๋ ์ค๋ฅธ์ชฝ ์ ๊ฐ๋ฝ์ ์์ํ ์ก์ ์ ์๋ค. -> ์ด๋ ๊ฒ Deadlock์ด ๋ฐ์
Starvation์ ๊ฒฝ์ฐ๋?
- ๊ฐ์ด๋ฐ ์ฌ๋์ด ๊ปด์ ์์ชฝ์ ์ฌ๋๋ง ๋ฐฅ์ ๋จน๊ณ ๊ฐ์ด๋ฐ ์ฌ๋์ ๋ฐฅ์ ๋จน์ง ๋ชปํ๋ starvation์ด ์๊ธธ ์ ์๋ค.
- Deadlock์ ํด๊ฒฐํ ์ ์๋ ๋ฐฉ๋ฒ(์๋ 3๊ฐ์ง ์กฐ๊ฑด ์ค ํ๋๋ง ์ถ๊ฐํ๋ฉด ํด๊ฒฐ ๊ฐ๋ฅํ๋ค. ํ์ง๋ง ์ด ๋ฐฉ๋ฒ์ starvation์ ๊ฐ๋ฅ์ฑ์ ์
์ ๋ ๊ฒ์ ์๋๋ค.)
- ์ต๋ 4๋ช ์ ์ฒ ํ์๋ง์ด ํ ์ด๋ธ์ ๋์์ ์์ ์ ์๊ฒ ํ๋ค.
- ํ ์ฒ ํ์๊ฐ ๋ ๊ฐ๋ฅผ ๋ชจ๋ ์ง์ ์ ์์ ๋๋ง ์ ๊ฐ๋ฝ์ ์ง๋๋ก ํ๋ค.
- ๋น๋์นญ ํด๊ฒฐ์์ ์ฌ์ฉํ๋ค. ex)ํ์๋ฒ์งธ ์ฒ ํ์๋ ์ผ์ชฝ ์ ๊ฐ๋ฝ๋ถํฐ ์ง์ด์ผ ํ๊ณ ์ง์๋ฒ์งธ ์ฒ ํ์๋ ์ค๋ฅธ์ชฝ ์ ๊ฐ๋ฝ๋ถํฐ ์ง์ด์ผ ํ๋ค.
- Monitor solution
monitor DiningPhilosophers{
enum {THINKING; HUNGRY; EATING} state[5];
condition self[5]; // shared data์ ์์ญ์์ ํ๋ก์ธ์ค์ ๋๊ธฐ์ํ๋ฅผ ์ค์ ํ๊ธฐ ์ํ ๋ณ์
void pickup(int i){
state[i] = HUNGRY; // ์ ๊ฐ๋ฝ์ ๋ค๋ฉด ํด๋น ์ฒ ํ์์ ์ํ๋ ๋ฐฐ๊ณ ํ ์ํ๋ก ์ค์
test(i);
if(state[i] != EATING)
self[i].wait; // ๋ค๋ฅธ ์ฒ ํ์๊ฐ eatingํ๊ณ ์์ด์ ํด๋น state์ ์ํ๋ฅผ wait๋ก ์ค์
}
void putdown(int i){
state[i] = THINKING; // ํด๋น ์ฒ ํ์๋ ๋ค์ ์ํ๋ฅผ ์๊ฐํ๋ ๊ฒ์ผ๋ก ์ค์
//test left and right neightbors
test((i+4) % 5); // ์ผ์ชฝ ์ฒ ํ์์๊ฒ ์๊ทธ๋์ ๋ณด๋ธ๋ค.
test((i+1) % 5); // ์ค๋ฅธ์ชฝ ์ฒ ํ์์๊ฒ ์๊ทธ๋์ ๋ณด๋ธ๋ค.
}
void test(int i){
if ((state[(i+4) % 5] != EATING) &&
(state[i] ==HUNGRY) &&
(state[(i+1) % 5] != EATING)) { // ์ผ์ชฝ ์ฒ ํ์์ ์ค๋ฅธ์ชฝ ์ฒ ํ์๊ฐ ์๊ฐํ๋ ์ํ์ด๊ณ ๋์ ์ํ๊ฐ ๋ฐฐ๊ณ ํ ์ํ์ผ๋
state[i] = EATING; // ๋์ ์ํ๋ฅผ ๋จน๋ ์ํ๋ก ์ ํ
self[i].signal(); // ํด๋น ์ฝ๋๋ putdown์์ ์ฌ์ฉ ๊ฐ๋ฅ
}
}
void initialization_code(){
for(int i = 0; i<5; i++)
state[i] = THINKING;
}
}
monitor๋ฅผ ์ด์ฉํ dining philosophers problem
- ๊ฐ๊ฐ์ ์ฒ ํ์ i๋ ์๋์ ๊ฐ์ด pickup()๊ณผ putdown() operation์ ์คํํ๋ค.
DiningPhilosopher.pickup(i)
...
/** EAT **/
...
DiningPhilosopher.putdown(i);
์ด ํด๊ฒฐ๋ฒ์ Deadlock์ ๋ฐ์ํ์ง ์์ง๋ง starvation์ ์ผ์ด๋ ์ ์๋ค.
2. Synchronization within the Kernel
์ด ๋ถ๋ถ์์๋ linux์ ์ํด ์ ๊ณต๋๋ ๋๊ธฐํ ๋ฉ์ปค๋์ฆ์ ๋ํด์ ์ค๋ช ํ๋ค.
- Synchronization in Linux
- Linux : 2.6 ๋ฒ์ ์ด์ ์๋ ๋น์ ์ ํ ์ปค๋์ด์๋ค. ์ด๋ kernel mode์์ ์๋ํ๋ ํ ํ๋ก์ธ์ค๊ฐ ์ ์ ๋ ์ ์๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค. ํ์ง๋ง ์ง๊ธ์ linux kernel์ ์์ ํ ์ ์ ํ์ด๋ค.
- Linux๋ Semaphores(kernel์์ locking์ ์ํด), atomic integers(์ํ์ฐ์ฐ์ ์ํ), spinlocks(kernel์์ locking์ ์ํด), ๊ทธ๋ฆฌ๊ณ reader-writer version of both์ ์ ๊ณตํ๋ค.
- Atomic variables
- ์ปดํจํฐ ์ํคํ ์ณ๊ฐ ๊ฐ๋จํ ์ํ ์ฐ์ฐ์ ์ํด atomin versions์ ๋ํด ๋ช ๋ น์ด๋ฅผ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์, Linux kernel ๋ด์์ ๊ฐ์ฅ ๊ฐ๋จํ ๋๊ธฐํ ๊ธฐ๋ฒ
- atomic_t๋ atomic integer์ ํ์ ์ด๋ค.
- ๋ชจ๋ math operation์ interruption์์ด ์ํ๋๋ค.
- ex) atomic_t counter; int value; // ์๋์ ์ฝ๋๋ atomic integer counter๋ฅผ ์ ์ธํ๊ณ ๋ค์ํ atomic์ฐ์ฐ์ ์ํํ๋ ๊ฒ์ ๋ณด์ฌ์ค๋ค.
- Mutex locks
- kernel ๋ด์ critical section์ ๋ณดํธํ๊ธฐ ์ํด์ mutex lock์ ์ฌ์ฉํ๋ ๊ฒ์ด linux์์ ๊ฐ๋ฅํ๋ค.
- int mutex_init(struct mutex *lock); // initialize the mutex
- int mutex_lock(struct mutex *lock); // acquire the mutex, critical section์ ๋ค์ด๊ฐ๊ธฐ ์ ์ ๊ผญ ํธ์ถํด์ผ๋ง ํ๋ค. ๋ง์ฝ mutex lock์ด ์ด์ฉ๊ฐ๋ฅํ์ง ์๋ค๋ฉด mutex_lock()์ ํธ์ถํ task๋ sleep state์ ๋ค์ด๊ฐ๊ณ , lock์ ์ฃผ์ธ์ด mutex_unlock()์ ๋ถ๋ฅผ ๋ ๊นจ์ด๋๋ค.
- int mutex_unlock(struct mutex *lock); // Release the mutex, must be invoked after exiting the critical section, If the mutex lock is unavailable, a task calling mutex_lock() is put into sleep state and is awakened when the lock's owner invokes mutex_unlock()
- Spinlocks(spinlock์ ๋ค๋ฅธ ์ค๋ ๋๊ฐ lock์ ์์ ํ๊ณ ์๋ค๋ฉด ๊ทธ lock์ด ๋ฐํ๋ ๋๊น์ง ๊ณ์ ํ์ธํ๋ฉฐ ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ด๋ค)
- spin_lock(), spin_unlock(), etc.
- SMP machines์์, ๊ธฐ๋ณธ locking mechanism์ spinlock์ด๊ณ , ๊ทธ kernel์ spinlock์ด ์งง์ ์๊ฐ๋์๋ง ๋ณด์ ๋๋๋ก ์ค๊ณ๋๋ค.
- ์ค์ง ๋จ์ผ ํ๋ก์ธ์ฑ ์ฝ์ด๋ฅผ ๊ฐ์ง ์๋ฒ ๋๋ ์์คํ ๊ฐ์ ๋จ์ผ ํ๋ก์ธ์ ๋จธ์ ์์ spinlock์ ์ฌ์ฉํ๊ธฐ์ ๋ถ์ ์ ํ๊ณ , kernel ์ ์ ์ ํ์ฑํ/ ๋นํ์ฑํ ํ์ฌ ๋์ฒด๋๋ค.
- Enabling/disabling kernel preemption
- Linux๋ kernel ์ ์ ์ ๋นํ์ฑํํ๊ณ , ํ์ฑํ ํ๋๋ฐ ํฅ๋ฏธ๋ก์ด ์ ๊ทผ๋ฒ์ ์ฌ์ฉํ๋ค. ๊ทธ๊ฒ์ ์ปค๋ ์ ์ ์ ๋นํ์ฑํ ํ๊ณ ํ์ฑํ ํ๋๋ฐ ๊ฐ๋จํ system calls์ ์ฌ์ฉํ๋ค.
- preempt_disable(), preempt_enable()
Single Processor | Multiple Processors |
Disable kernel preemption | Acquire spin lock |
Enable kernel preemption | Release spin lock |
3. POSIX Synchronization
POSIX API๋ 1) Mutex locks 2) Semaphores 3) Condition variable์ ์ ๊ณตํ๋ค.
UNIX, Linux, macOS์์ ๋๋ฆฌ ์ฌ์ฉ๋๋ค.
1) POSIX Mutex Locks
- Creating and initializing the lock
#include <pthread.h>
pthread_mutex_t mutex; // global declaration
//create and initialize the mutex lock
pthread_mutex_init(&mutex, NULL); //call before first lock
or
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- Acquiring and releasing the lock
pthread_mutex_lock(&mutex); // acquire the mutex lock
/* critical section */
pthread_mutex_unlock(&mutex); // release the mutex lock
2) POSIX Semaphores
- Named semaphores
- Multiple unrelated processes๋ common semaphore๋ฅผ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
- Creating and initializing the lock
#include <semaphore.h>
sem_t *sem; // global declaration
//create the semaphore and initialize it to 1
sem = sem_open("SEM", O_CREAT, 0666, 1);
- Acquiring and releasing the lock
sem_wait(sem); // acquire the semaphore
/* critical section */
sem_post(sem); // release the semaphore
- Unnamed semaphores
- Creating and initializing the lock
#include <semaphore.h>
sem_t sem; // global declaration
//Create the semaphore and initialize it to 1
sem_init(&sem, 0, 1);
- Acquiring and releasing the lock
sem_wait(&sem); // acquire the semaphore
/* critical section */
sem_post(&sem); // release the semaphore
3) POSIX Condition Variables
POSIX๋ ์ผ๋ฐ์ ์ผ๋ก C/C++์์ ์ฌ์ฉ๋๊ณ ์ด๋ฌํ ์ธ์ด๋ ๋ชจ๋ํฐ๋ฅผ ์ ๊ณตํ์ง ์๋๋ค.
POSIX condition variables์ POSIX mutex lock๊ณผ ์ฐ๊ด๋์ด mutual exclution์ ๋ณด์ฅํ๋ค.
- Creating and initializing
pthread_mutex_t mutex;
pthread_cond_t cond_var;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond_var, NULL);
- Example
- ์กฐ๊ฑด a==b๊ฐ true๊ฐ ๋๋ ๊ฒ์ ๊ธฐ๋ค๋ฆฌ๋ ์ค๋ ๋
pthread_mutex_lock(&mutex);
while(a!=b)
pthread_cond_wait(&cond_var, &mutex);
pthread_mutex_unlock(&mutex);
- condition variable์ ๋๊ธฐ์ค์ธ ๋ค๋ฅธ ์ค๋ ๋์๊ฒ ์๊ทธ๋์ ๋ณด๋ด๋ ์ค๋ ๋
pthread_mutex_lock(&mutex);
a = b;
pthread_cond_signal(&cond_var);
pthread_mutex_unlock(&mutex);
4. Synchronization in Java
Java๋ ๊ฐ์ฅ ๋ง์ synchronization feature์ ์ ๊ณตํ๋ค.
- Java monitors
- Reentrant locks
- Semaphores
- Condition variables
1) Java Monitors
Java์ ๋ชจ๋ ๊ฐ์ฒด๋ lock์ ๊ฐ์ง๊ณ ์๋ค.
๋จผ์ ์๋ ์ฝ๋๋ฅผ ๋ณด์
public class BoundedBuffer<E>
{
private static final in BUFFER_SIZE = 5;
private int count, in, out;
private E[] buffer;
public BoundedBuffer(){
count = 0;
in = 0;
out = 0;
buffer = (E[]) new Object[BUFFER_SIZE];
}
//Producers call this method
public synchronized void insert(E item){
while(count == BUFFER_SIZE){
try{
wait();
}catch{
InterruptedExceprion ie
}
buffer[in] = item;
in = (in + 1) % BUFFER_SIZE;
count++;
notify();
}
}
//Consumers call this method
public synchronized E remove(){
E item;
while(count == 0){ //๋ฒํผ๊ฐ ๋น์ด์์ผ๋ฉด
try{
wait();
}catch{
InterruptedException ie
}
item = buffer[out];
out = (out + 1) % BUFFER_SIZE;
count--;
notify();
return item;
}
}
}
- ๋ง์ฝ ํ method๊ฐ synchronized๋ก ์ ์ธ๋์ด์๋ค๋ฉด, ๊ทธ method๋ฅผ ํธ์ถํ ์ค๋ ๋๊ฐ ๊ทธ object์ ๋ํ lock์ ์ป์ด์ผ ํ๋ค.
- ๋ง์ฝ ๋ค๋ฅธ ์ค๋ ๋๊ฐ ๊ทธ lock์ ์คํํ๊ณ ์๋ค๋ฉด, ํธ์ถํ ์ค๋ ๋๋ ๊ทธ ์ค๋ ๋๊ฐ ๋๋ ๋๊น์ง ๋๊ธฐํด์ผ ํ๋ค.
- Locks are released when the owning thread exits the synchronized method
- ์ด์ฉ๋ถ๊ฐ๋ฅํ lock์ ์ป๊ธธ ์๋ํ๋ ์ค๋ ๋๋ ๊ฐ์ฒด์ entry set์ ๋ค์ด๊ฐ๋ค.
- ๋ง์ฝ lock์ ๊ฐ์ง ์ค๋ ๋๊ฐ ๊ณ์ํ ์ ์์ผ๋ฉด wait()๋ฅผ ํธ์ถํ๋ค.
- ํ ์ค๋ ๋๊ฐ wait()ํจ์์ ํธ์ถํ์ ๋:
- ๊ทธ ์ค๋ ๋๋ ๊ฐ์ฒด์ lock์ ํผ๋ค.
- ๊ทธ ์ค๋ ์ค์ ์ํ๊ฐ '์ฐจ๋จ'์ผ๋ก ์ค์ ๋๋ค.
- ๊ทธ ์ค๋ ๋๋ wait set์ผ๋ก ๋ฐฐ์น๋๋ค.
- ํ ์ค๋ ๋๊ฐ wait()ํจ์์ ํธ์ถํ์ ๋:
- ์ค๋ ๋๋ ๋๊ธฐํ๊ณ ์๋ ์ค๋ ๋๋ฅผ ๊นจ์ฐ๊ธฐ ์ํด notify()๋ฅผ ํธ์ถ ํ ์ ์๋ค.
- ํ ์ค๋ ๋๊ฐ notify()๋ฅผ ํธ์ถํ์ ๋:
- ์์์ ์ค๋ ๋ T๊ฐ wait set์ผ๋ก ๋ถํฐ ์ ํ๋๋ค.
- T๋ wait set์์ entry set์ผ๋ก ์ด๋ํ๋ค.
- T์ ์ํ๋ ์ฐจ๋จ๋จ์์ runnable์ํ๋ก ๋ฐ๋
- ๊ทธ๋ฌ๋ฉด T๋ ์ด์ condition์ด true์ธ์ง ํ์ธํ๋ lock๋ค๊ณผ ๊ฒฝ์ํ ์ ์๋ค.
- ํ ์ค๋ ๋๊ฐ notify()๋ฅผ ํธ์ถํ์ ๋:
2) Java Reentrant Locks
Reentrant Lock์ mutex๋ก ์๊ฐํ๋ฉด ๋๋ค.
Lock key = new ReentrantLock();
key.lock();
try{
//critical section
}finally{
key.unlock();
}
์ฌ๊ธฐ์ finally ๋ถ๋ถ์ ์์ธ๊ฐ try block์์ ๋ฐ์ํ ๊ฒฝ์ฐ์ lock์ด release ๋ ๊ฒ์ด๋ผ๋ ๊ฒ์ ๋ณด์ฅํ๋ค.
3) Java Semaphores
- Constructor
Semaphore(int value);
- Usage
Semaphore sem = new Semaphore(1);
try{
sem.acquire();
//critical section
}
catch(InterruptedException ie){ }
finally{
sem.release();
}
4) Java Condition Variables
- Condition variables์ ReentrantLock๊ณผ ๊ด๋ จ์ด ์๋ค.
- ReentrantLock์ 'newCondition()' method๋ฅผ ์ด์ฉํ์ฌ condition variable์ ๋ง๋ค ์ ์๋ค.
Lock key = new ReentrantLock();
Condition condVar = key.newCondition();
- ํ ์ค๋ ๋๋ 'await()'๋ผ๋ ๋ฉ์๋๋ฅผ ํธ์ถํจ์ผ๋ก์จ ๋๊ธฐํ๋ฉฐ, 'signal()' ๋ฉ์๋๋ฅผ ํธ์ถํจ์ผ๋ก์จ ์ ํธ๋ฅผ ๋ณด๋ธ๋ค.
- Example
- 0~4๊น์ง์ 5๊ฐ์ง ์ค๋ ๋๊ฐ ์กด์ฌํ๋ค๊ณ ํ์.
- Shared variable์ธ 'turn'์ด ์กด์ฌํ๋ค. ์ด๋ ์ด๋ค ์ค๋ ๋์ ์์์ธ์ง ๊ฐ๋ฆฌํจ๋ค.
- ์ค๋ ๋๋ ์ด๋ค ์์ ์ ํ๊ณ ์ถ์ ๋, dowork()๋ฅผ ํธ์ถํ๋ค. (ํ์ง๋ง ์ด๊ฒ์ ์ค์ง ๊ทธ ์ค๋ ๋์ ์์์ผ๋๋ง ๊ทธ ์ค๋ ๋๊ฐ ์์ ์ ํ ์ ์๋ค. )
- ๋ง์ผ ๊ทธ ์ค๋ ๋์ turn์ด ์๋๋ผ๋ฉด ๋๊ธฐํ๋ค.
- ๋ง์ฝ ๊ทธ ์ค๋ ๋์ turn์ด๋ผ๋ฉด, ์ ์ ๊ทธ ์ผ์ ์ํํ๋ค.
- ๊ทธ ์ผ์ด ๋๋ฌ๋ค๋ฉด, ๊ทธ ํด์ ๋ค์์ธ ์ค๋ ๋์๊ฒ notifyํ๋ค.
- ํ์์ ์ธ data structure๋ค์ ์๋์ ๊ฐ๋ค.
Lock key = new ReentrantLock();
Condition[] condVars = new Condition[5];
for(int i = 0; i < 5; i++)
condVars[i] = lock.newCondition();
public void doWork(int threadNumber){
lock.lock();
try{
//If it's not my turn, then wait until I'm signaled
if(threadNumber != turn)
condVars[threadNumbers].await();
//Do some work for awhile...
//Now signal to the next thread
turn = (turn + 1) % 5;
condVars[turn].signal();
}
catch(InterruptedException ie){ }
finally{
lock.unlock();
}
}
5. Alternative Approaches(๋์์ ์ ๊ทผ)
- Memory transaction
- ๋ถ๋ฌ์ค๊ธฐ์ ์ ์ฅํ๊ธฐ ๋ช ๋ น(a sequence of memory read-write operations)์ ์งํฉ์ด atomicํ๊ฒ ์ํ๋ ์ ์๊ฒ ํจ์ผ๋ก์ ๋ณํ์ฑ ํ๋ก๊ทธ๋๋ฐ์ ๋จ์ํ๊ฒ ํ๋ ๋ฐฉ์์ด๋ค.
- tramsaction์ atomic{s}( S์ ์๋ statement๊ฐ atomicํ๊ฒ ์คํ๋๋๋ก ๋ณด์ฅํด์ฃผ๋) ๋ฅผ ์ถ๊ฐํด์ค์ผ๋ก์จ ์คํ๋ ์ ์๋ค.
//Tranditional implementation
void update(){
acquire();
/* modify shared data */
release();
}
//Memory transaction-based implementation
void update(){
atomic{
/* modify shared data */
}
}
- OpenMP(Open Multi-Processing)
- ๊ณต์ ๋ฉ๋ชจ๋ฆฌ ๋ค์ค ์ฒ๋ฆฌ ํ๋ก๊ทธ๋๋ฐ API๋ก "#pragma omp critical"์ ํฌํจ๋ ์ฝ๋๋ critical section์ผ๋ก ๋ค๋ค์ง๋ฉฐ atomicํ๊ฒ ์ํ๋๋ค.
void update(int value){
#pragma omp critical
{
count += value;
}
}
'CS > Operating System' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Operating System] ์ด์์ฒด์ Caching [2] (0) | 2024.03.09 |
---|---|
[Operating System] ์ด์์ฒด์ Caching [1] (0) | 2024.03.07 |
[์ด์์ฒด์ ] Chapter6. Synchronization Tools(๋๊ธฐํ) (0) | 2022.05.30 |