Post

[OS] Operating System(6-1): Critical section, peterson, Synchronization Hardware

[OS] Operating System(6-1): Critical section, peterson, Synchronization Hardware

๐Ÿ€ ์šด์˜์ฒด์ œ ์ „๊ณต ์ˆ˜์—… ์ •๋ฆฌ

ํ”„๋กœ์„ธ์Šค๋“ค์ด ๋™์‹œ์— ์‹คํ–‰๋˜๋ฉด์„œ ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค

์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๊ณต์œ  ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ๋–„ ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค

๊ทธ๋ž˜์„œ ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ์„ ์œ„ํ•ด์„œ๋Š” olderly execution์„ ๋ณด์žฅํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ํ•„์š”

์ด์— ๋Œ€ํ•œ ์˜ˆ์‹œ๋กœ Producer-Consumer Problem์ด ์žˆ๋‹ค

Producer-Consumer Problem

  • Producer: ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์‚ฐํ•ด์„œ ๋ฒ„ํผ์— ์ €์žฅ
  • Consumer: ๋ฒ„ํผ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉ
  • ๋ฒ„ํผ๋Š” ํ•œ์ •๋œ ํฌ๊ธฐ, ๊ฐ€๋“ ์ฐผ์„ ๋•Œ ์ƒ์‚ฐ์ž๋Š” ๋Œ€๊ธฐํ•ด์•ผํ•˜๊ณ , ๋น„์–ด์žˆ์„ ๋•Œ ์†Œ๋น„์ž๋Š” ๋Œ€๊ธฐ
  • counter๋ณ€์ˆ˜: ํ˜„์žฌ ๋ฒ„ํผ์— ์žˆ๋Š” ์•„์ดํ…œ์˜ ์ˆ˜๋ฅผ ์ถ”์ 
Producer code
1
2
3
4
5
6
7
8
9
10
while (true) {
    /* produce an item in next_produced */
    
    while (counter == BUFFER_SIZE)
        ; /* do nothing */ // "busy waiting"
    
    buffer[in] = next_produced;
    in = (in + 1) % BUFFER_SIZE;
    counter++;
}
  • ์ƒ์‚ฐ์ž๋Š” ๋ฒ„ํผ๊ฐ€ ๊ฐ€๋“ ์ฐผ๋Š”์ง€ ํ™•์ธํ•˜๊ณ (counter == BUFFER_SIZE), ๊ฐ€๋“ ์ฐผ๋‹ค๋ฉด ๋Œ€๊ธฐ
  • ๊ณต๊ฐ„์ด ์žˆ์œผ๋ฉด ์•„์ดํ…œ์„ ๋ฒ„ํผ์— ์ถ”๊ฐ€ํ•˜๊ณ , in ํฌ์ธํ„ฐ๋ฅผ ๋‹ค์Œ ์œ„์น˜๋กœ ์ด๋™
Consumer code
1
2
3
4
5
6
7
8
9
10
while (true) {
    while (counter == 0)
        ; /* do nothing */ // "busy waiting"
    
    next_consumed = buffer[out];
    out = (out + 1) % BUFFER_SIZE;
    counter--;
    
    /* consume the item in next_consumed */
}

Race Condition


๐Ÿ“šRace Condition: ๋‘ ๊ฐœ ์ด์ƒ์˜ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋™์‹œ์— ๊ณต์œ  ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋ ค๊ณ  ํ•  ๋•Œ, ์ ‘๊ทผ ์ˆœ์„œ์— ๋”ฐ๋ผ ์‹คํ–‰ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ
โ†’ ์ด๋กœ ์ธํ•ด ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜ ๋ฐœ์ƒ!

counter ์˜ˆ์‹œ

counter++์™€ counter--๋Š” ์‹ค์ œ๋กœ ์›์ž์ ์ด์ง€ ์•Š๋‹ค. ๊ธฐ๊ณ„์–ด๋กœ ๋ฒˆ์—ญํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • counter++:
1
2
3
load:   register1 = counter
inc:    register1 = register1 + 1
store:  counter = register1
  • counter--:
1
2
3
load:   register2 = counter
dec:    register2 = register2 - 1
store:  counter = register2

alt text

์ •์ƒ๊ฒฐ๊ณผ๋Š” 5์ด์–ด์•ผํ•จ
ํ•˜์ง€๋งŒ ์ด ๋‘ ๋ช…๋ น์–ด๋Š” ๋™์‹œ์— ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒ!

fork() ์˜ˆ์‹œ

  • ํ”„๋กœ์„ธ์Šค P0, P1์ด fork()๋กœ ์ž์‹ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ƒ์„ฑํ–ˆ์„ ๋•Œ, ๋™์‹œ์— ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋ฉด ๋™์ผํ•œ PID๋ฅผ ๋ฐ›๊ฒŒ ๋œ๋‹ค
  • ์ด๋Š” ๋™์ผํ•œ PID๊ฐ€ ๋‘ ๊ฐœ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค์— ํ• ๋‹น๋˜๋Š” ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒํ•œ๋‹ค.

alt text

  • ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋Š” mutual exclusion(์ƒํ˜ธ๋ฐฐ์ œ)์ด ํ•„์š”ํ•˜๋‹ค

Critical Section


๐Ÿ“šCritical Section: ์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๊ณต์œ ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋‚˜ ์ž์›์— ์ ‘๊ทผํ•˜๋Š” ์ฝ”๋“œ ์˜์—ญ(์ ‘๊ทผ๋งŒ์ด ์•„๋‹ˆ๋ผ ์‚ฌ์šฉํ•ด์•ผํ•จ)

  • ์ด ์˜์—ญ์—์„œ๋Š” ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์Šค๋งŒ ์‹คํ–‰๋จ
  • Critial Section์—์„œ ํ”„๋กœ์„ธ์Šค๋Š” ๊ณต์œ  ๋ณ€์ˆ˜ ๋ณ€๊ฒฝ, ํ…Œ์ด๋ธ” ์—…๋ฐ์ดํŠธ, ํŒŒ์ผ ์“ฐ๊ธฐ ๋“ฑ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰

โœ…ํ”„๋กœ์„ธ์Šค์˜ ์ผ๋ฐ˜์ ์ธ ๊ตฌ์กฐ:

1
2
3
4
5
6
do {
    entry section       // ์ง„์ž… ์˜์—ญ
    critical section    // ์ž„๊ณ„ ์˜์—ญ
    exit section        // ํ‡ด์ถœ ์˜์—ญ
    remainder section   // ๋‚˜๋จธ์ง€ ์˜์—ญ
} while (true);
  • entry section: critical section์— ๋“ค์–ด๊ฐ€๊ธฐ ์œ„ํ•œ ํ—ˆ๊ฐ€๋ฅผ ์š”์ฒญํ•˜๋Š” ๋ถ€๋ถ„
  • critical section: ๊ณต์œ  ์ž์›์— ์ ‘๊ทผํ•˜๋Š” ๋ถ€๋ถ„
  • exit section: ์ž„๊ณ„์˜์—ญ ๋ฒ—์–ด๋‚  ๋•Œ ์‹คํ–‰๋˜๋Š” ๋ถ€๋ถ„
  • remainder section: ๊ทธ ์™ธ ๋‚˜๋จธ์ง€ ์ฝ”๋“œ ๋ถ€๋ถ„

Critical Section solution


  1. Mutual Exclusion
    • ํ•œ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ž„๊ณ„์˜์—ญ์—์„œ ์‹คํ–‰ ์ค‘์ด๋ฉด, ๋‹ค๋ฅธ ์–ด๋–ค ํ”„๋กœ์„ธ์Šค๋„ ์ž„๊ณ„์˜์—ญ์—์„œ ์‹คํ–‰ X
  2. Progress
    • ์ž„๊ณ„์˜์—ญ์—์„œ ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์—†๊ณ , ๋ช‡๋ช‡ ํ”„๋กœ์„ธ์Šค๋“ค์ด ์ž„๊ณ„์˜์—ญ์— ๋“ค์–ด๊ฐ€๊ธธ ์›ํ•œ๋‹ค๋ฉด, ๋‹ค์Œ์— ์–ด๋–ค ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ž„๊ณ„์˜์—ญ์— ๋“ค์–ด๊ฐˆ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ด ๋ฌดํ•œ์ • ์—ฐ๊ธฐ๋˜์–ด์„œ๋Š” ์•ˆ ๋˜๊ฒŒ๋” ํ•ด์•ผํ•จ
  3. Bounded Waiting
    • ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ž„๊ณ„์˜์—ญ์— ๋“ค์–ด๊ฐ€๊ธฐ ์œ„ํ•œ ์š”์ฒญ์„ ํ•œ ํ›„, ๊ทธ ์š”์ฒญ์ด ํ—ˆ์šฉ๋˜๊ธฐ ์ „์— ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๋“ค์ด ์ž„๊ณ„์˜์—ญ์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋Š” ํšŸ์ˆ˜์— ํ•œ๊ณ„๊ฐ€ ์žˆ์–ด์•ผํ•จ
    • starvation(๊ธฐ์•„ ํ˜„์ƒ)์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•จ

Critical Section Handling in OS


์šด์˜์ฒด์ œ์—์„œ ์ž„๊ณ„์˜์—ญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ปค๋„์ด ์„ ์ ํ˜•(preemptive)์ธ์ง€ ๋น„์„ ์ ํ˜•(non-preemptive)์ธ์ง€์— ๋”ฐ๋ผ ๋‹ค๋ฅด๋‹ค.

  • Preemptive Kernel
    • ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ปค๋„ ๋ชจ๋“œ์—์„œ ์‹คํ–‰ ์ค‘์ผ ๋•Œ๋„ ํ”„๋กœ์„ธ์Šค๋ฅผ preemptive ๊ฐ€๋Šฅ
    • ์ฆ‰, ๋†’์€ ์šฐ์„ ์ˆœ์œ„์˜ ํ”„๋กœ์„ธ์Šค๊ฐ€ ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ค‘๋‹จ์‹œํ‚ค๊ณ  CPU๋ฅผ ์ฐจ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค
    • ๋‹จ์ : SMP(Symmetric Multi-Processing) ์•„ํ‚คํ…์ฒ˜์—์„œ ์„ค๊ณ„ํ•˜๊ธฐ ์–ด๋ ต๋‹ค
  • Non-preemptive Kernel
    • ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ปค๋„ ๋ชจ๋“œ๋ฅผ ๋น ์ ธ๋‚˜๊ฐ€๊ฑฐ๋‚˜, ๋ธ”๋ก ๋˜๋Š” ์ž๋ฐœ์ ์œผ๋กœ CPU๋ฅผ ์–‘๋ณดํ•  ๋•Œ๊นŒ์ง€ ์‹คํ–‰ ๋œ๋‹ค.
    • ์ปค๋„ ๋ชจ๋“œ์—์„œ ์‹ค์ง์ ์ธ race condition์ด ์—†๋‹ค.
    • ๋‹จ์ : ์‹ค์‹œ๊ฐ„ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ์ ํ•ฉํ•˜์ง€ ์•Š์Œ

๋‹จ์ผ ์ฝ”์–ด ์‹œ์Šคํ…œ์—์„œ๋Š” interrupt disabling์„ ์‚ฌ์šฉํ•ด์„œ critical section์„ ๋ณดํ˜ธํ•  ์ˆ˜ ์žˆ๋‹ค.
โ†’ ํ•˜์ง€๋งŒ ๋‹ค์ค‘์ฝ”์–ด/ํ”„๋กœ์„ธ์Šค ์—์„œ๋Š” ํšจ์œจ์„ฑ์ด ๋–จ์–ด์ ธ์„œ ์“ฐ์ง€ ์•Š๋Š”๋‹ค.

  • ๊ทธ๋ž˜์„œ preemptive kernel์„ ์„ ํ˜ธ!!

Petersonโ€™s Solution


๐Ÿ“šPetersonโ€™s Solution: ์ž„๊ณ„์˜์—ญ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜

  • ํ˜„๋Œ€ ์•„ํ‚คํ…์ฒ˜์—์„œ๋Š” ๋ณ„๋กœ์ด์ง€๋งŒ, ์•Œ๊ณ ๋ฆฌ์ฆ˜์  ์ ‘๊ทผ๋ฒ•์œผ๋กœ๋Š” ํ›Œ๋ฅญํ•จ

โœ…์ฃผ์š” ํŠน์ง•:

  • ๋‘ ๊ฐœ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋Œ€์ƒ
  • ๊ธฐ๊ณ„์–ด load, store ๋ช…๋ น์ด atomic์ด๋ผ๊ณ  ๊ฐ€์ •
    • atomic: ๋ฐ”๋€Œ์ง€ ์•Š๋Š” ์„ฑ์งˆ
  • ๋‘ ๊ฐœ์˜ ํ”„๋กœ์„ธ์Šค๋Š” ๋‘ ๊ฐœ์˜ ๋ณ€์ˆ˜๋ฅผ ๊ณต์œ :
    • int turn: ์–ด๋–ค ํ”„๋กœ์„ธ์Šค์˜ ์ฐจ๋ก€์ธ์ง€
    • boolean flag[2]: ๊ฐ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ž„๊ณ„์˜์—ญ์— ๋“ค์–ด๊ฐˆ ์ค€๋น„๊ฐ€ ๋˜์—ˆ๋Š”์ง€

โœ…์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌํ˜„:

1
2
3
4
5
6
7
8
9
10
11
12
while (true) {
    flag[i] = true;       // ๋‚˜ ์ž„๊ณ„์˜์—ญ์— ๋“ค์–ด๊ฐˆ ์ค€๋น„๊ฐ€ ๋์–ด
    turn = j;             // ์ƒ๋Œ€๋ฐฉ์—๊ฒŒ ์ฐจ๋ก€๋ฅผ ์–‘๋ณดํ•ด
    while (flag[j] && turn == j)
        ;                 // ์ƒ๋Œ€๋ฐฉ์ด ์ค€๋น„๋˜์–ด ์žˆ๊ณ , ์ƒ๋Œ€๋ฐฉ ์ฐจ๋ก€๋ฉด ๋Œ€๊ธฐ
    
    /* critical section */ // ์ž„๊ณ„์˜์—ญ
    
    flag[i] = false;      // ์ž„๊ณ„์˜์—ญ ์‚ฌ์šฉ ์™„๋ฃŒ
    
    /* remainder section */ // ๋‚˜๋จธ์ง€ ์˜์—ญ
}
  1. flag[i]=true๋กœ ์„ค์ •ํ•ด์„œ ์ž„๊ณ„์˜์—ญ์— ๋“ค์–ด๊ฐ€๊ณ  ์‹ถ๋‹ค๊ณ  ํ‘œ์‹œ
  2. turn ๋ณ€์ˆ˜๋ฅผ ์ƒ๋Œ€๋ฐฉ ํ”„๋กœ์„ธ์Šค ๋ฒˆํ˜ธ j๋กœ ์„ค์ •ํ•˜์—ฌ โ€œ์•™๋ณดโ€
  3. ์ดํ›„ ์ƒ๋Œ€๋ฐฉ์˜ ํ”Œ๋ž˜๊ทธ flag[j] == true์ด๊ณ  turn==j์ด๋ฉด ๋Œ€๊ธฐ
  4. ์ž„๊ณ„์˜์—ญ์„ ์‹คํ–‰ํ•œ ํ›„์—๋Š” ์ž์‹ ์˜ ํ”Œ๋ž˜๊ทธ flag[i] == false๋กœ ์„ค์ •ํ•˜์—ฌ ์ž„๊ณ„์˜์—ญ ์‚ฌ์šฉ์ด ๋๋‚ฌ์Œ์„ ํ‘œ์‹œ

ํ˜„๋Œ€ ์•„ํ‚คํ…์ณ์—์„œ๋Š” ์ œ๋Œ€๋กœ ์ž‘๋™X

ํ”„๋กœ์„ธ์„œ๋‚˜ ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์„ฑ๋Šฅํ–ฅ์ƒ์„ ์œ„ํ•ด ์˜์กด์„ฑ ์—†๋Š” ์—ฐ์‚ฐ๋“ค์˜ ์ˆœ์„œ๋ฅผ ์žฌ๋ฐฐ์น˜ํ•จ
โ†’ instruction reordering ๋ฌธ์ œ ๋ฐœ์ƒ ๋‹จ์ผ์Šค๋ ˆ๋“œ์—์„œ๋Š” ๊ดœ์ฐฎ์ง€๋งŒ ๋‹ค์ค‘์Šค๋ ˆ๋“œ์—์„œ ๋ฌธ์ œ ๋ฐœ์ƒ

instruction reordering ์˜ˆ์‹œ

1
2
flag[i] = true;
turn = j;

ํ”ผํ„ฐ์Šจ ์•Œ๊ณ ๋ฆฌ์ฆ˜์— ์œ„์™€ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์กด์žฌ ์ปดํŒŒ์ผ๋Ÿฌ๋‚˜ ํ”„๋กœ์„ธ์„œ๊ฐ€ ๋‘ ๋ช…๋ น์–ด์˜ ์ˆœ์„œ๋ฅผ ๋ฐ”๊พธ๋ฉด:

1
2
turn = j;
flag[i] = true;

์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค alt text

  1. ํ”„๋กœ์„ธ์Šค P0: turn = 1 ์‹คํ–‰ ํ›„ flag[0] = true ์‹คํ–‰
  2. ํ”„๋กœ์„ธ์Šค P1: turn = 0 ์‹คํ–‰ ํ›„ flag[1] = true ์‹คํ–‰

๊ฒฐ๊ณผ์ ์œผ๋กœ flag[0] = true, flag[1] = true, turn = 0์ด ๋˜์–ด P0๋Š” while ์กฐ๊ฑด์„ ํ†ต๊ณผํ•˜๊ณ , P1๋„ turn = 0์ด๋ฏ€๋กœ while ์กฐ๊ฑด์„ ํ†ต๊ณผํ•จ ์ฆ‰, ๋‘ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋™์‹œ์— critical section์— ์ง„์ž…ํ•˜๋Š” ์ƒํ˜ธ ๋ฐฐ์ œ ์œ„๋ฐ˜์ด ๋ฐœ์ƒ

Synchronization Hardware


๊ทธ๋Ÿผ software์ธก๋ฉด์—์„œ์˜ cs๋ฌธ์ œ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ๋ง๊ณ  hardware์  ํ•ด๊ฒฐ์ฑ…์„ ์•Œ์•„๋ณด์ž

CS๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ์œ„ํ•ด ํ˜„๋Œ€ ์ปดํ“จํ„ฐ๋Š” ๋‹ค์–‘ํ•œ ํ•˜๋“œ์›จ์–ด ๋™๊ธฐํ™” ๊ธฐ๋ฒ•์„ ์ œ๊ณตํ•จ

โœ…๊ทธ ์ด์œ  - uniprocessors ์‹œ์Šคํ…œ์˜ ํ•œ๊ณ„

  • ๋‹จ์ผ ํ”„๋กœ์„ธ์„œ ์‹œ์Šคํ…œ์—์„œ๋Š” disable interrupts ๋ฐฉ์‹์œผ๋กœ cs๋ฅผ ๋ณดํ˜ธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์ฝ”๋“œ๋Š” preemption ์—†์ด ์‹คํ–‰๋จ
  • ํ•˜์ง€๋งŒ ์ด ๋ฐฉ์‹์€ ๋‹ค์ค‘ ํ”„๋กœ์„ธ์„œ ์‹œ์Šคํ…œ์—์„œ๋Š” ๋น„ํšจ์œจ์ !
  • ๋˜ํ•œ ํ™•์žฅ์„ฑ(scalability)์ด ๋–จ์–ด์ง
ํ•˜๋“œ์›จ์–ด ์ง€์›์˜ 3๊ฐ€์ง€ ํ˜•ํƒœ:
  1. Memory barriers
  2. Hardware instructions
  3. Atomic variables

Memory Barriers


๐Ÿ“šMemory Barriers: ๋‹ค์ค‘ ํ”„๋กœ์„ธ์„œ ํ™˜๊ฒฝ์—์„œ ๋ฉ”๋ชจ๋ฆฌ ์—ฐ์‚ฐ์˜ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜

Memory Model
  • Memory Model: ์ปดํ“จํ„ฐ ์•„ํ‚คํ…์ฒ˜๊ฐ€ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์— ์ œ๊ณตํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ณด์žฅ(memory guarantees)์„ ์˜๋ฏธ

โœ…Memory model์€ 2๊ฐ€์ง€๋กœ ๋‚˜๋‰จ:

  1. Strongly ordered: ํ•œ ํ”„๋กœ์„ธ์Šค์—์„œ ๊ฐ’ ๋ณ€๊ฒฝ์ด ์™„๋ฃŒ๋  ๋•Œ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์„œ์— ์ฆ‰์‹œ ๋ณด์ž„
  2. Wekly ordered: ๊ฐ’ ๋ณ€๊ฒฝ์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „์—๋ผ๋„ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ๋ณด์—ฌ์คŒ, ์ฆ‰ ๋ฐ”๋กœ ๋ณด์ด์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Œ
  • Memory barrier: ๋ฉ”๋ชจ๋ฆฌ์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ๋ชจ๋“  ํ”„๋กœ์„ธ์„œ์— ์ „ํŒŒ(propagated)๋˜๋„๋ก ๊ฐ•์ œํ•˜๋Š” ๋ช…๋ น์–ด(=memory fence)
    • ํ˜„์žฌ ์ง„ํ–‰ ์ค‘์ธ load์™€ store๊ฐ€ ์™„๋ฃŒ๋œ ํ›„์— ์ƒˆ๋กœ์šด load์™€ store๊ฐ€ ์ˆ˜ํ–‰๋˜๋„๋ก ํ•จ
  • memory barrier ํ™œ์šฉ ์˜ˆ์ œ
1
2
3
4
5
6
7
8
9
10
11
12
13
// ๊ณต์œ  ๋ฐ์ดํ„ฐ
boolean flag = false;
int x = 0;

// ์Šค๋ ˆ๋“œ 1
while (!flag)
    memory_barrier();  // x ๊ฐ’์ด ๋กœ๋”ฉ๋˜๊ธฐ ์ „์— flag ๊ฐ’์ด ๋กœ๋”ฉ๋˜๋Š” ๊ฒƒ์„ ๋ณด์žฅ
print x;

// ์Šค๋ ˆ๋“œ 2
x = 100;
memory_barrier();  // x = 100์ด flag = true๋ณด๋‹ค ๋จผ์ € ์™„๋ฃŒ๋˜๋Š” ๊ฒƒ์„ ๋ณด์žฅ
flag = true;
  • memory_barrier๋Š” โ€œx ๊ฐ’์ด ๋กœ๋”ฉ๋˜๊ธฐ ์ „์— flag ๊ฐ’์ด ๋กœ๋”ฉ๋˜๋Š” ๊ฒƒ์„ ๋ณด์žฅโ€
  • x = 100์ด flag = true๋ณด๋‹ค ๋จผ์ € ์™„๋ฃŒ๋˜๋Š” ๊ฒƒ์„ ๋ณด์žฅ
  • Memory barrier๋Š” very low-level operation์œผ๋กœ ์ปค๋„ ๊ฐœ๋ฐœ์ž๊ฐ€ mutual exclusion ์ฝ”๋“œ๋ฅผ ํŠน๋ณ„ํžˆ ์ž‘์„ฑํ•  ๋•Œ๋งŒ ์‚ฌ์šฉ

Hardware Instruction


๐Ÿ“šHardware Instruction: cs๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ special hardware instructions alt text

โœ…ํŠน์ง•:

  • ์›์ž์  ์‹คํ–‰ ๋ณด์žฅ: ๋ช…๋ น์–ด๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋™์•ˆ ์ธํ„ฐ๋ŸฝํŠธ ๋ถˆ๊ฐ€(uninterrupt)
  • ์›Œ๋“œ ๋‚ด์šฉ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜, ๋‚ด์šฉ ๊ตํ™˜ ๊ฐ€๋Šฅ(test,modify,swap)
  • ํ˜„๋Œ€ ํ”„๋กœ์„ธ์„œ์—์„œ ๋„๋ฆฌ ์ง€์›๋จ

๋Œ€ํ‘œ์ ์œผ๋กœ 3๊ฐ€์ง€ ๋ช…๋ น์–ด๊ฐ€ ์กด์žฌ:

  1. Test-and-Set instruction
  2. Compare-and-Swap instruction
  3. Compare-and-Exchange instruction

Test-and-Set instruction

  • Test-and-Set instruction: ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์„ค์ •ํ•˜๋Š” ์—ฐ์‚ฐ์„ ์›์ž์ ์œผ๋กœ ์ˆ˜ํ–‰
1
2
3
4
5
boolean test_and_set(boolean *target) {
    boolean rv = *target;
    *target = true; //target์„ TRUE๋กœ ์„ค์ •
    return rv; // ์„ค์ • ์ „ target๊ฐ’์„ return
}

โœ…ํŠน์ง•:

  1. ์›์ž์ ์œผ๋กœ(atomically) ์‹คํ–‰๋จ
  2. ์ „๋‹ฌ๋œ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ์›๋ž˜ ๊ฐ’์„ return
  3. ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐ’์„ true๋กœ ์„ค์ •
Test-and-Set์„ ์ด์šฉํ•œ critical section ํ•ด๊ฒฐ์ฑ…
1
2
3
4
5
6
7
8
9
10
11
// ๊ณต์œ  boolean ๋ณ€์ˆ˜ lock, ์ดˆ๊ธฐ๊ฐ’์€ false
do {
    while (test_and_set(&lock)) //spin lock
        ; /* ๋ฐ”์œ ๋Œ€๊ธฐ (busy waiting) */
    
    /* critical section */
    
    lock = false;
    
    /* remainder section */
} while (true);
  • test_and_set(&lock): lock์˜ ํ˜„์žฌ ๊ฐ’์„ return, lock์„ true๋กœ ์„ค์ •
  • return๊ฐ’์ด true: ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ด๋ฏธ cs์˜์—ญ์— ์žˆ๋‹ค๋Š” ์˜๋ฏธ โ†’ ๋Œ€๊ธฐํ•จ
  • return๊ฐ’์ด false: CS์— ์•„๋ฌด๋„ ์—†๋‹ค๋Š” ์˜๋ฏธ, lock์„ ์ด๋ฏธ TRUE๋กœ ๋งŒ๋“ค์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๊ฐ€ CS์— ๋“ค์–ด์˜ค์ง€ ๋ชปํ•จ. ๋”ฐ๋ผ์„œ ์ž์‹ ์ด CS์— ๋“ค์–ด๊ฐ
  • cs๋ฅผ ๋น ์ ธ๋‚˜์˜ฌ ๋•Œ lock์„ false๋กœ ์„ค์ • โ†’ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค cs ์ง„์ž… ๊ฐ€๋Šฅ

์ด ๋ฐฉ์‹์€ ์Šคํ•€ ๋ฝ(spin lock)์ด๋ผ๊ณ ๋„ ๋ถˆ๋ฆฌ๋ฉฐ, ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋ฝ์„ ํš๋“ํ•  ๋•Œ๊นŒ์ง€ ๊ณ„์† CPU๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ ๋Œ€๊ธฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— CPU ์ž์›์„ ๋‚ญ๋น„ํ•  ์ˆ˜ ์žˆ์Œ

Compare-and-Swap instruction

  • Compare-and-Swap instruction: ๋ฉ”๋ชจ๋ฆฌ ์œ„์น˜์˜ ๋‚ด์šฉ๊ณผ ์ฃผ์–ด์ง„ ๊ฐ’์„ ๋น„๊ตํ•˜๊ณ , ๊ฐ™์œผ๋ฉด ์ƒˆ ๊ฐ’์œผ๋กœ ๊ต์ฒด
    1
    2
    3
    4
    5
    6
    
    int compare_and_swap(int *value, int expected, int new_value) {
      int temp = *value;
      if (*value == expected)
          *value = new_value;
      return temp;
    }
    

โœ…ํŠน์ง•:

  1. ์›์ž์ ์œผ๋กœ(atomically) ์‹คํ–‰๋จ
  2. value ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ์›๋ž˜ ๊ฐ’์„ return
  3. value๊ฐ€ expected ๊ฐ’๊ณผ ๊ฐ™์„ ๋•Œ๋งŒ value๋ฅผ new_value๋กœ ์„ค์ •
Compare-and-Swap์„ ์ด์šฉํ•œ critical section ํ•ด๊ฒฐ์ฑ…:
1
2
3
4
5
6
7
8
9
10
11
// ๊ณต์œ  int ๋ณ€์ˆ˜ lock, ์ดˆ๊ธฐ๊ฐ’์€ 0
while (true) {
    while (compare_and_swap(&lock, 0, 1) != 0) //spin lock
        ; /* ๋ฐ”์œ ๋Œ€๊ธฐ (busy waiting) */
    
    /* critical section */
    
    lock = 0;
    
    /* remainder section */
}
  • compare_and_swap(&lock, 0, 1): lock์˜ ๊ฐ’์ด 0์ด๋ฉด 1๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ์›๋ž˜ ๊ฐ’(0)์„ ๋ฐ˜ํ™˜
  • lock์˜ ๊ฐ’์ด 0์ด ์•„๋‹ˆ๋ฉด ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ํ˜„์žฌ ๊ฐ’(1)์„ ๋ฐ˜ํ™˜
  • ๋ฐ˜ํ™˜๊ฐ’์ด 0์ด ์•„๋‹ˆ๋ฉด ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ด๋ฏธ ์ž„๊ณ„์˜์—ญ์— ์žˆ์œผ๋ฏ€๋กœ ๋Œ€๊ธฐ
  • ๋ฐ˜ํ™˜๊ฐ’์ด 0์ด๋ฉด ์ž„๊ณ„์˜์—ญ์— ๋“ค์–ด๊ฐ

Compare-and-Exchange intruction

Compare-and-Exchange instruction : Compare-and-Swap์˜ ํ™•์žฅ๋œ ๋ฒ„์ „, ๊ฐ’์ด ์ผ์น˜ํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์˜ˆ์ƒ ๊ฐ’์„ ์—…๋ฐ์ดํŠธ

1
2
3
4
5
6
7
8
9
bool compare_and_exchange(int *value, int *expected, int new_value) {
    if (*value == *expected) {
        *value = new_value;
        return true;
    } else {
        *expected = *value;
        return false;
    }
}

โœ…ํŠน์ง•:

  1. ์›์ž์ ์œผ๋กœ(atomically) ์‹คํ–‰๋จ
  2. value๊ฐ€ expected ๊ฐ’๊ณผ ๊ฐ™์œผ๋ฉด value๋ฅผ new_value๋กœ ์„ค์ •ํ•˜๊ณ  true ๋ฐ˜ํ™˜
  3. ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด expected๋ฅผ value์˜ ์‹ค์ œ ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๊ณ  false ๋ฐ˜ํ™˜
Compare-and-Exchange๋ฅผ ์ด์šฉํ•œ ์ž„๊ณ„์˜์—ญ ํ•ด๊ฒฐ์ฑ…
1
2
3
4
5
6
7
8
9
10
11
12
// ๊ณต์œ  int ๋ณ€์ˆ˜ lock, ์ดˆ๊ธฐ๊ฐ’์€ 0
while (true) {
    expected = 0;
    while (!compare_and_exchange(&lock, &expected, 1)) //spin lock
        expected = 0;
    
    /* critical section */
    
    lock = 0;
    
    /* remainder section */
}

์•ž์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜๋“ค์€ starvation์„ ๋ฐฉ์ง€ ๋ชปํ•  ์ˆ˜๋„ ์žˆ๋‹ค!!

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Compare-and-Swap์„ ์‚ฌ์šฉํ•ด์„œ bounded-waiting์„ ๋ณด์žฅํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ์žˆ๋‹ค.

Bounded-waiting Mutual Exclusion with CAS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
while (true) {
    waiting[i] = true;
    key = 1;
    while (waiting[i] && key == 1)
        key = compare_and_swap(&lock, 0, 1);
    
    waiting[i] = false;
    
    /* critical section */
    
    j = (i + 1) % n;
    while ((j != i) && !waiting[j])
        j = (j + 1) % n;
    
    if (j == i) // ์•„๋ฌด๋„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ํ”„๋กœ์„ธ์Šค๊ฐ€ ์—†๋‹ค๋Š” ๋œป
        lock = 0; // lock์„ ํ‘ผ๋‹ค
    else
        //cs์— ๋“ค์–ด๊ฐ€๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ํ”„๋กœ์„ธ์Šค๊ฐ€ ์žˆ์–ด์„œ lock์„ ๊ฑด ์ƒํƒœ์—์„œ ๋“ค์–ด๊ฐ€๋„๋ก ํ•จ
        waiting[j] = false; 
    
    /* remainder section */
}
  • waiting[i] = true: ํ”„๋กœ์„ธ์Šค i๊ฐ€ ์ž„๊ณ„์˜์—ญ์— ๋“ค์–ด๊ฐ€๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ์Œ์„ ํ‘œ์‹œ
  • key = compare_and_swap(&lock, 0, 1)์„ ํ†ตํ•ด lock์„ ํš๋“ํ•˜๋ ค๊ณ  ์‹œ๋„
    • key=1: lock์ด ๊ฑธ๋ ค์žˆ์Œ(๋ˆ„๊ตฐ๊ฐ€ cs์— ์žˆ์Œ)
    • key=0: cs์ง„์ž… ๊ฐ€๋Šฅ
  • ์ž„๊ณ„์˜์—ญ์„ ๋‚˜์˜ฌ ๋•Œ, ๋Œ€๊ธฐ ์ค‘์ธ ๋‹ค์Œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฐพ์•„ waiting ์ƒํƒœ๋ฅผ false๋กœ ๋ฐ”๊ฟ”์คŒ
  • ๋Œ€๊ธฐ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์—†์œผ๋ฉด lock์„ 0์œผ๋กœ ์„ค์ •

์ด ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ๋Œ€๊ธฐ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค๋“ค์ด ์ˆœ์„œ๋Œ€๋กœ ์ž„๊ณ„์˜์—ญ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณด์žฅ

Atomic Variables


๐Ÿ“šAtomic Variables: ์ธํ„ฐ๋ŸฝํŠธ ์—†์ด ์›์ž์ ์œผ๋กœ(atomically) ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋Š” ํŠน๋ณ„ํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž…, CAS ๊ฐ™์€ ํ•˜๋“œ์›จ์–ด ๋ช…๋ น์–ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„๋จ

โœ…ํŠน์ง•::

  • integer, boolean ๊ฐ™์€ basic data type์— ๋Œ€ํ•œ ์›์ž์  ์—ฐ์‚ฐ ์ œ๊ณต
  • ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ/ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•ด๋„ ์ผ๊ด€์„ฑ ์žˆ๋Š” ๊ฒฐ๊ณผ ๋ณด์žฅ

atomic variable ์ฃผ์š” ์—ฐ์‚ฐ

  1. ์ฝ๊ธฐ/์“ฐ๊ธฐ ์—ฐ์‚ฐ
    • load(): ์›์ž์  ๋ณ€์ˆ˜์˜ ํ˜„์žฌ ๊ฐ’์„ ์ฝ์Œ
    • store(val): ์›์ž์  ๋ณ€์ˆ˜์— ๊ฐ’์„ ์”€
  2. ์ฆ๊ฐ€/๊ฐ์†Œ ์—ฐ์‚ฐ
    • increment(): ์›์ž์  ๋ณ€์ˆ˜์˜ ๊ฐ’์„ 1 ์ฆ๊ฐ€์‹œํ‚ด
    • decrement(): ์›์ž์  ๋ณ€์ˆ˜์˜ ๊ฐ’์„ 1 ๊ฐ์†Œ์‹œํ‚ด
    • fetch_add(val): ํ˜„์žฌ ๊ฐ’์— val์„ ๋”ํ•˜๊ณ  ์ด์ „ ๊ฐ’์„ ๋ฐ˜ํ™˜
    • fetch_sub(val): ํ˜„์žฌ ๊ฐ’์—์„œ val์„ ๋นผ๊ณ  ์ด์ „ ๊ฐ’์„ ๋ฐ˜ํ™˜
  3. ๋น„๊ต ๋ฐ ๊ตํ™˜ ์—ฐ์‚ฐ
    • compare_exchange_strong(expected, desired):
      • ํ˜„์žฌ ๊ฐ’์ด expected์™€ ๊ฐ™์œผ๋ฉด desired๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  true ๋ฐ˜ํ™˜
      • ๋‹ค๋ฅด๋ฉด expected๋ฅผ ํ˜„์žฌ ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๊ณ  false ๋ฐ˜ํ™˜
    • compare_exchange_weak(expected, desired):
      • strong ๋ฒ„์ „๊ณผ ์œ ์‚ฌํ•˜์ง€๋งŒ ํ•˜๋“œ์›จ์–ด ์ œํ•œ์œผ๋กœ ์ธํ•ด โ€œ๊ฑฐ์ง“ ์‹คํŒจโ€๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ
      • ์ผ๋ถ€ ํ”Œ๋žซํผ์—์„œ ๋” ํšจ์œจ์ ์ด์ง€๋งŒ, ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฃจํ”„ ๋‚ด์—์„œ ์‚ฌ์šฉ๋จ

Increment func ๊ตฌํ˜„

1
2
3
4
void increment(atomic_int *v) {
    int temp = *v;
    while (!compare_and_exchange(v, &temp, temp+1));
}
  1. ํ˜„์žฌ ๊ฐ’ v๋ฅผ ์ž„์‹œ ๋ณ€์ˆ˜ temp์— ์ €์žฅ
  2. compare_and_exchange๋ฅผ ์‚ฌ์šฉ โ†’ ํ˜„์žฌ ๊ฐ’์ด temp์™€ ๊ฐ™์€์ง€ ํ™•์ธ, ๊ฐ™๋‹ค๋ฉด ๊ฐ’์„ temp+1๋กœ ์—…๋ฐ์ดํŠธ
  3. ์„ฑ๊ณตํ•˜๋ฉด ๋ฃจํ”„ ์ข…๋ฃŒ
  4. ์‹คํŒจํ•˜๋ฉด(๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ’์„ ๋ณ€๊ฒฝํ–ˆ๋‹ค๋ฉด) compare_and_exchange๋Š” temp๋ฅผ ํ˜„์žฌ ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๊ณ , ๋ฃจํ”„๊ฐ€ ๋‹ค์‹œ ์‹คํ–‰

Atomic_Compare&Exchange(C11)

  • atomic_compare_exchange_strong(object, expected, desired):
    • if object == expected โ†’ object โ† desired, true return
    • if object != expected โ†’ expected โ† object, false return
    • ๊ฑฐ์ง“ ์‹คํŒจ ๋ฐœ์ƒ X
  • atomic_compare_exchange_weak(object, expected, desired):
    • strong๊ณผ ๋™์ž‘์€ ๊ฐ™์ง€๋งŒ object == expected์—์„œ false๋ฅผ ๋ฆฌํ„ดํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Œ(๊ฑฐ์ง“ ์‹คํŒจ)
    • ํ•˜์ง€๋งŒ ๋ฐœ์ƒ ํ™•๋ฅ ์ด ๋‚ฎ๊ณ  ์ƒ๋Œ€์ ์œผ๋กœ ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๊ฑฐ์ง“ ํŒ๋‹จ์ด ํฌ๊ฒŒ ์ค‘์š”์น˜ ์•Š์€ while๋ฌธ์— ์‚ฌ์šฉ

strong/weak ์˜ˆ์‹œ:

1
2
3
4
5
6
7
8
9
10
11
12
atomic_int lock = 0;
int expected = 0;

// Strong ๋ฒ„์ „: if๋ฌธ ์•ˆ์—์„œ ์‚ฌ์šฉํ•  ๋•Œ ์ ํ•ฉ
if (atomic_compare_exchange_strong(&lock, &expected, 1)) {
    // ์ž„๊ณ„์˜์—ญ ์ง„์ž… ์„ฑ๊ณต
}

// Weak ๋ฒ„์ „: while ๋ฃจํ”„ ์•ˆ์—์„œ ์‚ฌ์šฉํ•  ๋•Œ ์ ํ•ฉ
while (!atomic_compare_exchange_weak(&lock, &expected, 1)) {
    expected = 0;  // ์‹คํŒจ ์‹œ expected ๊ฐ’ ์žฌ์„ค์ •
}
This post is licensed under CC BY 4.0 by the author.