Loading [MathJax]/jax/output/CommonHTML/jax.js
본문 바로가기

[운영체제] 6주차: Process synchronzation

Review

race condition

공유 데이터 access 시 접근 순서에 따라 결과가 달라지는 상태

 

Race condition이 발생한다는 것은 공유 데이터가 있다는 것. 이 부분을 Critical section이라 함

 

여러 프로세스가 동시에 Critical section에 접근해서 data가 깨지기 때문에 발생하는 문제임

-> 한 번에 한 프로세스만 접근할 수 있도록 한다.

 

이 문제를 해결하려면 다음의 세 조건을 충족해야 함

Mutual exclusion

Critical section에 접근하는 프로세스는 한 시점에 한 개뿐이어야 함

 

Progress

Critical section에 진입할 준비가 된 프로세스들만을 대상으로 다음에 접근할 프로세스가 정해짐

 

Bounded waiting

Critical section에 진입할 준비가 됐으면 일정 시간 후에는 반드시 진입해야 함

-> 프로세스들 간의 상대적인 속도를 가정해선 안 된다.

 

Peterson's solution

LOAD와 STORE는 atomic하게 작동한다.

 

 

Semaphore

wait

semaphore 값이 0 이하면 루프를 돈다.

아니면 semaphore 값을 1 줄인다.

 

signal

semaphore 값을 1 늘린다.

 

wait과 signal은 atomic하다.

 

Binary semaphore: Semaphore 값이 0 아님 1 (Mutex lock과 같음)

Counting semaphore:  Semaphore 값이 0..N

 

Critical section 문제와 Process synchronization 문제를 해결할 수 있다.

 

Deadlock: 잘못 사용

Starvation: 잘못 구현

 

 

Semaphore problem

 

Priority inversion

우선순위가 낮은 프로세스가 lock을 가져서(자원을 점유해서) 우선순위가 높은 프로세스가 실행되지 못 하는 문제

우선 순위는 A > B > C다.

① C가 실행되는 중 wait()를 호출 해 sem-A를 획득한다.

② B의 우선순위가 더 높아 C의 자원이 B에 할당된다. C는 block된다.

③ A의 우선순위가 더 높아 B의 자원이 A에 할당된다.

④ A도 critical section에 진입하기 위해 sem-A를 요청한다.

⑤ 그런데 이 sem-A는 현재 C에게 있으므로 A는 wait(sem-A) 호출 후(sem-A 요청 후) block된다.

⑥ 이제 우선순위가 높은 B가 실행된다.

 

따라서 C보다 우선순위가 높은 프로세스가 계속 ready queue에 올려지면 C는 sem-A를 가진 상태로

계속 block되므로 결국 A도 실행이 안 된다.

 

우선순위 계승(Priority inheritance Protocol)

어떤 프로세스가 우선 순위가 높은 프로세스가 요구하는 자원을 가지고 있을 때

그 프로세스의 우선순위를 자원을 요구하는 프로세스의 우선순위로 높이는 방법

 

ⓐ 어떤 프로세스가 자원을 사용 중인데 다른 프로세스가 이 자원을 요청하면 요청한 프로세스는 block된다.

ⓑ 자원이 사용가능하면 프로세스는 자원을 할당받는다.

ⓒ 우선순위 계승이 발생한다.

ⓓ 우선순위를 계승받은 프로세스가 자원을 반환하면 원래 우선순위로 낮아진다.

 

① C가 실행되다가 wait() 호출 -> sem-A 획득

② A가 wait() 호출

③ 현재 sem-A는 C에게 있으므로 C의 우선순위를 A의 우선순위로 높임

④ C 실행 (C의 우선순위는 1)

⑤ C가 완료(signal())되면 C의 우선순위는 다시 5가 됨, 이제 A가 sem-A를 획득해서 실행

 

 

 

 

Monitor

Semaphore로 문제를 해결할 때 문제가 발생하는지 아닌지는 전적으로 프로그래머에게 달려있다.

Ex) wait() 후 signal 해야 되는데 반대로 함, Semaphore의 초기값을 잘못 줌

-> 프로세스 간 실행 순서가 뒤죽박죽 되거나 Critical section 문제가 해결되지 않을 수 있음

 

Monitor는 이런 오류를 근본적으로 줄이는 방법으로, 프로그래밍 언어에서 제공되는 문법이다.

Ex) Java: synchronized

Java의 경우 메소드에 synchronized 키워드가 붙으면

여러 thread가 이 메소드를 호출해도 한 번에 한 스레드만 호출할 수 있게 한다.

-> 별도로 wait() 하고 어쩌고 할 필요 없음

 

Monitor의 구현 방법은 여러가지가 있는데, 다음이 한 예시다.

일종의 pseudocode라고 보면 됨

Syntax of a monitor

Monitor는 세 부분으로 구성될 수 있다

ⓐ 함수: 여러 개의 프로세스가 이 함수를 호출해도 한 번에 한 프로세스만 접근함을 보장한다.

ⓑ 공유변수:

ⓒ 공유변수 초기화 부분

 

signal()로 공유변수에 대한 접근을 반환하는 방식으로 프로그램을 작성해야 하는데,

이 부분은 프로그래머에 전적으로 의존한다. 따라서 이 부분을 해결하기 위해 Monitor를 사용하는 것이다.

 

- Critical section problem

여러 개의 프로세스가 동시에 Monitor의 메소드를 호출하면 이 프로세스들은 Queue에 저장된 후

한 프로세스씩 Critical section에 접근하게 된다.

 

 

- Process synchronization

Monitor는 프로세스 간 동기화 문제 (실행 순서 문제)는 해결할 수 없다.

따라서 condition 변수를 추가한다.

 

 

Condition variables

p.wait()

pp.signal()이 호출될 때까지 suspend(보류)된다 (Queue에 올려진다).

 

p.signal()

wait()를 호출한 프로세스 중 하나를 wakeup 시킨다.

이런 프로세스가 없는 경우 아무 동작도 하지 않는다.

 

* 여기서 waitsignal은 Semaphore의 그것과 아무 관련이 없다. 그냥 이름만 같은 거임

 

 

 

 

각 condition 변수는 Monitor안에 존재하며 독자적인 queue를 갖는다.

x.wait()를 호출하면 x의 queue에 추가된다.

 

Constraint

여러 개의 프로세스(P1,P2)가 함수 f1()을 동시에 호출했다고 하자.

이 경우 Monitor 특성에 의해 둘 중 하나는 Queue에 올려진다. 여기서는 P1이 올려졌다고 하자.

그러면 P2가 실행 중일 것이다. 여기서 P2x.wait()를 호출했다고 하자. 이제 P2는 x의 queue에 올려진다.

이 상태에서 어떤 조건에 의해 P2가 queue에서 나오게 되면 P1P2가 동시에 f1()에 접근하게 된다.

 

이번에는 P2가 Queue에 올려졌다고 하자. 그러면 P1f1()에 접근 중일 것이다.

이 상태에서 P1이 x.wait()을 호출했다고 하자. 이제 P1은 x의 queue에 올려진다. 이제 P2가 queue에서 나와 f1()에 접근 중일 것이다.

이 상태에서 P2가 x.signal()을 호출했다고 하자. 그러면 P1은 전에 실행되던 지점부터 다시 실행되는데, 이 지점이 f1()이다.

따라서 두 프로세스가 동시에 f1()에 접근하게 된다.

 

이 문제를 해결방안은 다음과 같다.

프로세스 P와 Q가 있으며 P는 x.signal()을 호출했고 Q는 x.wait()으로 인해 x의 queue에서 대기 중이라고 하자.

ⓐ Signal and wait: P가 x.signal() 호출 후 x.wait()을 호출해 자기 자신을 queue에 올린다.

ⓑ Signal and continue: P가 x.signal() 호출 후 Q를 condition에서 꺼내서 실행시키는 게 아니라 ready queue에 넣는다.

 

 

mutex

Mutual exclusion 보장을 위한 semaphore

 

next_count

condition의 signal 호출 후 기다리고 있는 프로세스의 수

 

next

관련된 semaphore

 

Monitor 내부에 존재하는 prodedure는 그 자체로 임계 구역이다.

따라서 prodecure에 진입하기 전에 wait(mutex)를 호출해 한 프로세스만 진입할 수 있도록 한다.

prodecure가 끝날 때는 signal을 호출한다.

 

 

 

x_sem

condition  변수 x를 가리키기 위한 변수

 

x_count

x.wait()를 호출한 프로세스의 수 = x의 queue에 있는 프로세스의 수

 

next_count > 0

x.signal 호출 후 기다리는 프로세스가 있음을 의미

 

else

x.wait() 후 프로시저 F에 진입할 프로세스가 없는 경우 반드시 signal(mutex)를 호출해서

다른 프로세스가 F 메소드로 진입할 수 있게 해야 함

 

wait(x_sem)

프로시저 종료 시 무조건 x.wait() 해줘야 함

 

x_count--

wait(x_sem)이 종료되었다는 것은 어떤 프로세스가 x.signal()을 호출했음을 의미함

따라서 x_count 값을 1 줄인다.

 

 

x_count

x의 queue에서 대기 중인 프로세스가 있을 경우

 

next_count++

자기자신은 wait하고 x의 queue에서 하나를 실행시키므로 next_count를 1 늘린다.

 

signal(x_sem)

x의 queue에서 대기 중인 프로세스를 wakeup 시키고

 

wait(next)

나 자신은 next 세마포어를 기다려야 한다.

P, Q가 있을 때 P가 x.wait()을 호출하면 Q를 wakeup시키고 자기 자신은 sleep 상태가 된다.

 

next_count--

위 작업이 다 끝나면 P를 wakeup하기 떄문에 next_count를 1 줄인다.

 

 

 

(나중에 정리할 부분)

 

 

여러 개의 프로세스에서 x.wait()을 호출해서 대기 중일 때 x.signal()은 어느 프로세스를 wake up시킬 것인가?

간단하게는 FCFS

-> 급하게 실행해야 되는 프로세스가 있다면?

=> Conditional wait: wait 할 때 자신의 우선순위까지 queue에 올린다

 

Single resource allocation

Monitor를 이용해 해결할 수 있다.

예를 들어 print 기능을 여러 프로세스가 한 번에 접근해 사용하면 이상한 게 출력될 수 있으므로 한 번에 한 프로세스만 print에 접근해야 한다.

R은 ResourceAllocator의 instance

acquire()

resource에 대한 사용권을 얻는다.

parameter t는 현재 resource를 사용중인 프로세스를 계속 기다릴 건지 일정시간만 기다릴 건지를 나타낸다.

 

release()

사용권을 release한다.

 

 

busy

공유 변수

이 변수가 TRUE면 현재 이 자원은 사용 중임을 의미함

최초에는 FALSE

 

① aPrinter.acquire(100) (A)

a가 먼저 printer에 대한 사용권을 얻고

 

② print out something (A)

출력한다

 

③ aPrinter.acquire(200)

A가 출력하는 중에 B가 사용권을 얻으려 한다.

그런데 이 때는 ①에 의해 busy가 TRUE이므로 B는 x.wait()을 호출한다.

 

④ aPrinter.release() (A)

busy를 FALSE로 바꾸고 x.signal()하면 B가 Printer를 사용하게 된다.

 

⑤ print out something (B)

⑥ aPrinter.release() (B)

기다리는 프로세스가 없으므로 그냥 종료

 

 

Summary

Race condition

Critical section

Atomicity: 그 operation 수행 중에는 interrupt가 발생하지 않는다.

 

Semaphore

Integer variables

wait, signal : atomic operation

 

Monitor

Critical section 문제는 해결 가능하나 Process synchronization은 해결 불가

-> Condition 변수: x.wait(), x.signal()