프로세스와 스레드는 동시다발적으로 실행되며 서로 협력하며 영향을 주고 받는다. 이 과정에서 자원의 알관성을 보장해야 한다. 즉 프로세스들의 동기화를 고려해야 한다.
동기화의 의미
공동의 목적을 위해서 다양한 프로세스들은 동시에 수행된다. 동기화란 이 프로세스들의 수행 시기를 맞추는 것이다. 크게 두 가지가 있다. (참고로 실행의 문맥을 갖는 모든 대상은 동기화 대상이기에 스레드도 동기화 대상이다.)
실행 순서 제어 : 프로세스를 올바른 순서대로 실행하기
상호 배제 : 동시에 접근해서는 안 되는 자원에 하나의 프로세스만 접근하게 하기
실행 순서 제어를 위한 동기화(Reader Writer Problem)
Writer는 파일에 값을 저장하는 프로세스이고 Reader는 파일에 저장된 값을 읽어들이는 프로세스라고 하자. Reader와 Writer 프로세스는 무작정 아무렇게나 실행되어서는 안된다. 실행의 순서가 있기 때문인데 Reader 프로세스는 파일 안에 값이 존재한다는 특정 조건이 만족되어야만 실행이 가능하기 때문이다. 따라서 Writer가 먼저 선행 되어야 한다.
상호 배제를 위한 동기화(Bank Account Problem)
한 번에 하나의 프로세스만 접근해야 하는 자원에 동시 접근을 피하기 위한 동기화이다. 예를 들어 현재 계좌에 잔액이 10만원이 있다고 하자. 그리고 두 가지의 프로세스가 있다.
- 프로세스 A : 계좌의 잔액을 읽어 들이고 읽어들인 잔액에 2만원을 더한 뒤 더한 값을 저장하는 프로세스이다.
- 프로세스 B : 계좌의 잔액을 읽어 들이고 읽어들인 잔액에 5만원을 더한 뒤 더한 값을 저장하는 프로세스이다.
프로세스 A,B를 동시에 무작정 실행했을 때 계좌에 17만원이 남아 있을까?
만약 동기화가 안되어 있다면
- 프로세스 A가 잔액을 읽어들인다.(10만원)
- 읽어 들인 값에서 2만원을 더한다.(12만원)
- 문맥 교환 발생
- 프로세스 B가 잔액을 읽어들인다.(10만원)
- 읽어들인 값에서 5만원을 더한다.(15만원)
- 문맥 교환 발생
- 프로세스 A가 더한 값 저장(잔액이 12만원으로 저장)
- 프로세스 B가 더한 값 저장(잔액이 다시 15만원으로 저장)
이렇기 때문에 한 번에 하나의 프로세스만 접근해야 하는 자원에 동시 접근을 피하기 위한 동기화가 필요하다.
두번째 예를 들어보자
Producer & Consumer Problem
물건을 계속해서 생산하는 생산자 (producer) 프로세스와 물건을 계속해서 소비하는 소비자 (consumer) 프로세스가 있다고 할 때 총합 변수를 공유한다고 하자. 생산자는 버퍼에 데이터를 삽입하고 총합 변수에서 1을 증가시킨다. 소비자는 반대로 버퍼에서 데이터를 빼내고 총합변수에서 1을 감소시킨다고 하자.
이 때 생산자 10만번, 소비자 10만번을 무작정 실행한다면 총합 변수는 0이 될까?

때로는 0과 다른 값이 되거나 오류가 발생하기도 한다. Bank Acount Problem과 동일한 문제가 발생하는 것이다.
즉 동기화가 되지 않았고 동시에 접근해서는 안되는 자원에 동시에 접근해서 발생한 것이다.
그럼 동시에 접근해서는 안되는 자원이란 뭘까?
동시에 접근해서는 안되는 자원
공유 자원
여러 프로세스 혹은 스레드가 공유하는 자원. 예를 들면 전역변수, 파일, 입출력장치, 보조기억장치 등이 있다.
임계 구역
동시에 실행하면 문제가 발생하는 자원에 접근하는 코드 영역. 앞선 예시의 총합, 전역 변수들을 예로 들 수 있겠다. 즉 문제를 피하려면 임계구역에 진입하고자 했을 때 진입한 프로세스 이외에는 대기해야 한다. 만약 임계 구역에 동시에 접근하면 앞선 예시들처럼 자원의 일관성이 깨질 수 있는데 이를 레이스 컨디션이라고 한다.
운영체제가 임계 구역 문제를 해결하는 세가지 원칙이 있는데 다음과 같다.(상호 배제를 위한 동기화를 위한 세가지 원칙)
- 상호 배제 : 한 프로세스가 임계 구역에 진입했다면 다른 프로세스는 들어올 수 없다.
- 진행 : 임계 구역에 어떤 프로세스도 진입하지 않았다면 진입하고자 하는 프로세스는 들어갈 수 있어야 한다.
- 유한 대기 : 한 프로세스가 임계 구역에 진입하고 싶다면 언젠가는 임계 구역에 들어올 수 있어야 한다. 즉 임계 구역에 들어오기 위해 무한정 대기해서는 안된다.
동기화 기법
동기화 기법은 뮤텍스 락, 세마포, 모니터 등이 있다.
뮤텍스 락
상호 배제를 위한 동기화 도구이다. 자물쇠 역할이라고 할 수 있다. 뮤텍스 락은 전역 변수 하나, 함수 두개로 이루어진 단순한 형태이다.
- 자물쇠 역할 - 프로세스들이 공유하는 전역 변수 lock
- 임계 구역을 잠그는 역할 - acquire 함수
- 임계 구역의 잠금을 해제하는 역할 - release 함수
acquire 함수는 프로세스가 임계 구역에 진입하기 전에 호출해서 임계구역이 잠겨 있다면 (lock 변수가 true라면) 임계 구역이 열릴 때까지 (lock 변수가 false가 될 때까지) 임계 구역을 반복적으로 확인하고 임계 구역이 열려 있다면 임계 구역을 잠근다(lock을 true로 바꾸기) - 이때 무한히 반복적으로 확인하는 것을 바쁜 대기(busy waiting)이라고 한다.
require 함수는 임계 구역에서의 작업이 끝나고 호출되며 현재 잠긴 임계 구역을 열게 된다.(lock을 false로 바꾸기)
세마포
좀 더 일반화된 방식의 동기화 도구로 공유 자원이 여러 개 있는 경우에도 적용이 가능하다. 임계 구역 앞에서 멈춤 신호를 받으면 잠시 기다리다가 가도 좋다는 신호를 받으면 임계 구역에 진입하는 방식이다.
세마포는 전역 변수 하나, 함수 두개의 단순한 형태로 이루어져 있다.
- 임계 구역에 진입할 수 있는 프로세스의 개수(사용 가능한 공유 자원의 개수)를 나타내는 전역 변수 S
- 임계 구역에 들어가도 좋은지, 기다려야 할지를 알려주는 wait 함수
- 임계 구역 앞에서 기다리는 프로세스에 이제 가도 좋다고 신호를 주는 signal 함수
wait 함수는 만일 임계 구역에 진입할 수 있는 프로세스 개수가 0 이하라면 사용할 수 있는 자원이 있는지 반복적으로 확인하고 임계 구역에 진입할 수 있는 프로세스 개수가 하나 이상이면 S를 1 감소 시키고 임계 구역에 진입한다.
signal 함수는 임계 구역에서 작업이 끝나고 S를 1 증가시킨다.
세 개의 프로세스 P1,P2,P3가 두 개의 공유 자원(S=2)에 P1,P2,P3 순서로 접근한다고 가정했을 때
- 프로세스 P1 wait 호출. S는 현재 2이므로 S를 1 감소시키고 임계 구역 진입
- 프로세스 P2 wait 호출. S는 현재 1이므로 S를 1 감소시키고 임계 구역 진입
- 프로세스 P3 wait 호출. S는 현재 0이므로 무한히 반복하며 S 확인
- 프로세스 P1 임계 구역 작업 종료, signal 함수 호출. S를 1 증가
- 프로세스 P3가 S가 1이 됨을 확인. S는 현재 1이므로 S를 1 감소시키고 임계 구역에 진입
여기서도 Busy Waiting이 발생한다. (CPU 사이클 낭비)
해결 방법으로는 사용할 수 있는 자원이 없을 경우 대기 상태로 만든다.(해당 프로세스의 PCB를 대기 큐에 삽입) 사용할 수 있는 자원이 생겼을 경우 대기 큐의 프로세스를 준비 상태로 만든다.(해당 프로세스의 PCB를 대기 큐에서 꺼내 준비 큐에 삽입)
wait 함수는 S를 확인해서 임계 구역에 진입할 수 없으면 해당 프로세스 PCB를 대기 큐에 삽입하고 대기 상태로 접어든다.
signal 함수는 대기 큐에 있는 프로세스를 제거하고 해당 프로세스를 대기 상태에서 준비 상태로 만든다.
이 방법으로 위 예시를 다시 한번 보자.
세 개의 프로세스 P1,P2,P3가 두 개의 공유 자원(S=2)에 P1,P2,P3 순서로 접근한다고 가정했을 때
- 프로세스 P1 wait 호출. S는 현재 2이므로 S를 1 감소시키고 임계 구역 진입
- 프로세스 P2 wait 호출. S는 현재 1이므로 S를 1 감소시키고 임계 구역 진입
- 프로세스 P3 wait 호출. S를 1 감소시키면 S는 -1이므로 본인의 PCB를 대기 큐에 넣고 대기 상태로 전환
- 프로세스 P1 임계 구역 작업 종료, signal 함수 호출. S를 1 증가하면 0이므로 대기 상태였던 P3를 대기 큐에서 꺼내 준비 큐로 옮겨줌.
- 깨어난 프로세스 P3 임계 구역 진입
- 프로세스 P2 임계 구역 작업 종료, signal 함수 호출. S를 1 증가시켜 S=1
- 프로세스 P3 임계 구역 작업 종료, signal 함수 호출. S를 1 증가시켜 S=2
세마포를 활용한 실행 순서 동기화
세마포의 변수 S를 0으로 두고 먼저 실행할 프로세스 뒤에 signal 함수, 다음에 실행할 프로세스 앞에 wait 함수를 붙이면 된다.
모니터
임계 구역 앞 뒤로 매번 wait, signal을 사용하기엔 번거롭다. 모니터는 사용자(개발자)가 다루기에 편한 동기화 도구이다.
상호 배제를 위한 동기화
- 인터페이스를 위한 큐
- 공유 자원에 접근하고자 하는 프로세스를 (인터페이스를 위한) 큐에 삽입
- 큐에 삽입된 순서대로 (한 번에 하나의 프로세스만) 공유 자원 이용
실행 순서를 위한 동기화
- 조건 변수 이용 - 프로세스나 스레드의 실행 순서를 제어하기 위해 사용하는 특별한 변수
- 조건 변수.wait() - 대기 상태로 변경, 조건 변수에 대한 큐에 삽입
- 조건 변수.signal() - wait()으로 대기 상태에 접어든 조건 변수를 실행 상태로 변경
모니터 안에는 하나의 프로세스만이 있을 수 있다. wait()를 호출했던 프로세스는 signal()을 호출한 프로세스가 모니터를 떠난 뒤에 수행을 재개하고 signal()을 호출한 프로세스의 실행을 일시 중단하고 자신이 실행된 뒤 다시 signal()을 호출한 프로세스의 수행을 재개한다.
즉 실행 순서 제어를 위한 동기화는
- 특정 프로세스가 아직 실행될 조건이 되지 않았을 때에는 wait를 통해 실행을 중단한다.
- 특정 프로세스가 실행될 조건이 충족되었을 때에는 signal을 통해 실행을 재개한다.
'CS' 카테고리의 다른 글
[CS] HTTP Method 종류 (0) | 2023.08.30 |
---|---|
[CS] 운영체제 (5) 교착 상태 (0) | 2023.07.28 |
[CS] 운영체제 (3) CPU 스케줄링 (0) | 2023.07.20 |
[CS] 운영체제 (2) 프로세스 (1) | 2023.07.14 |
[CS] 운영체제 (1) 운영체제란? (0) | 2023.07.13 |