들어가며
"세마포어와 뮤텍스의 차이점을 말해보세요."
어제까지만 해도 이 질문에 대답하기 어려웠다.
이 글을 보고 있는 당신도 아마 어제의 나와 비슷한 상태일 거라고 생각한다. (난 이제 아니다!)
대답하기 위해, 처음부터 알아보자.
목차
1. 세마포어란?
2. 세마포어의 연산
3. 세마포어의 분류
4. 뮤텍스
5. 세마포어와 뮤텍스의 차이점
세마포어와 뮤텍스
세마포어란?
두 개 이상의 프로세스들은 간단한 형태의 시그널을 이용해 협동할 수 있다.
한 프로세스가 특정 시그널을 수신할 때까지 정해진 위치에서 중지하도록 강제한다.
시그널을 위해 세마포어라고 불리는 특수 변수들을 사용한다.
두 개 이상의 프로세스들이 협동하기 위해 시그널이 필요하다.
시그널 발신, 수신을 위해 변수가 필요하다.
세마포어는 특수 변수다.
세마포어의 연산
세마포어는 오직 아래 세 가지 연산으로만 조작할 수 있다.
Initialize | Increase | Decrease | |
연산설명 | 세마포어 변수 초기화 연산 | 세마포어 값 1 증가 연산 | 세마포어 값 1 감소 연산 |
기타정보 | 음이 아닌 값으로 초기화 | semSignal(), semSignalB() | semWait(), semWaitB() |
이 세 가지 연산으로 세마포어가 어떻게 동작하는지,
지금부터 세마포어의 종류별로 그 동작방식을 살펴보자.
세마포어의 분류 1 - 값의 범위
세마포어는 변수에 유지할 수 있는 값의 범위에 따라 구분된다.
범용 세마포어 | 이진 세마포어 | |
값의 범위 | 정수 | 오직 0 또는 1 |
초기화 | 0 또는 양의 정수 | 0 또는 1 |
범용 세마포어
범용 세마포어는 General Semaphore, Counting Semaphore 라고도 불린다.
0 또는 양의 정수로 초기화되며, 세마포어에 담을 수 있는 값의 범위는 음수, 0, 양수로 정수만 가능하다.
위에서 소개한 세마포어를 다룰 수 있는 세 가지 연산을 이용하여, 범용 세마포어가 어떻게 동작하는지 살펴보자.
동영상을 보고, 설명을 읽어보면 이해가 될 것이다.
- 임계구역 상호배제를 위해 세마포어 변수 하나가 있다.
- 임계구역 실행을 원하는 P1, P2, P3 세 프로세스가 있다.
1. Semaphore 변수가 1로 초기화된다.
2. P1이 임계구역 실행을 위해 semWait() 함수를 호출한다.
3. 세마포어가 1 감소해 0이 된다.
4. 세마포어가 1 이었으므로 P1은 임계구역을 실행한다.
5. P1이 임계구역을 실행하는 도중, P2가 임계구역 실행을 위해 semWait() 함수를 호출한다.
6. 세마포어가 1 감소해 -1이 된다. *범용 세마포어가 음수인 경우, 세마포어 값의 절대값은 Blocked 상태의 프로세스 개수를 의미한다.
7. P2 프로세스는 Blocked 상태가 되어 큐에 들어간다.
8. P1이 임계구역 실행을 마치고 semSignal() 함수를 호출한다.
9. semSignal() 연산은 블록 상태의 프로세스가 있으면 프로세스를 깨운다. 이 과정에서 P2가 깨어난다.
10. 세마포어가 1 증가해 0이 된다.
11. P2가 임계구역을 실행한다.
12. P2가 임계구역을 실행하는 도중, P3가 임계구역 실행을 위해 semWait() 함수를 호출한다.
13. 세마포어가 1 감소해 -1이 된다.
14. P3 프로세스는 Blocked 상태가 되어 큐에 들어간다.
15. P2가 임계구역 실행을 마치고 semSignal() 함수를 호출한다.
16. semSignal() 함수는 Blocked 상태의 프로세스 P3을 깨우고 세마포어 변수를 1 증가시켜 세마포어 값이 0이 된다.
17. P3가 임계구역을 실행한다.
18. P3가 임계구역 실행을 마치고 semSignal() 함수를 호출한다.
19. Blocked 상태의 프로세스가 없으므로 semSignal() 함수는 세마포어 변수를 1 증가시키고, 세마포어 값은 1이 된다.
범용 세마포어는 세마포어 값의 증감을 통해
임계 영역에 진입할 수 있는 프로세스의 개수를 나타내주기 때문에 공유 자원의 인스턴스가 여러 개인 경우 효율적이다.
예) 3개의 화장실 칸을 5명이 이용하는 경우
이진 세마포어
이진 세마포어는 Binary Semaphore 라고도 불린다.
0 또는 1로 초기화되며, 세마포어에 담을 수 있는 값은 오직 0 또는 1만 가능하다.
위에서 소개한 세마포어를 다룰 수 있는 세 가지 연산을 이용하여, 이진 세마포어가 어떻게 동작하는지 살펴보자.
이번에도 동영상을 보고, 설명을 읽어보면 이해가 될 것이다.
- 임계구역 상호배제를 위해 세마포어 변수 하나가 있다.
- 임계구역 실행을 원하는 P1, P2, P3 세 프로세스가 있다.
1. Semaphore 변수가 1로 초기화된다.
2. P1이 임계구역 실행을 위해 semWaitB() 함수를 호출한다.
3. 세마포어가 1 감소해 0이 된다.
4. 세마포어가 1 이었으므로 P1은 임계구역을 실행한다.
5. P1이 임계구역을 실행하는 도중, P2가 임계구역 실행을 위해 semWaitB() 함수를 호출한다.
6. 세마포어가 0이므로 P2 프로세스는 Blocked 상태가 되어 큐에 들어간다.
7. P1이 임계구역 실행을 마치고 semSignal() 함수를 호출한다.
8. semSignal() 연산은 블록 상태의 프로세스가 있으면 프로세스를 깨운다. 이 과정에서 P2가 깨어난다.
* 여기서 세마포어 변수는 1로 증가하지 않는다.
9. P2가 임계구역을 실행한다.
10. P2가 임계구역을 실행하는 도중, P3가 임계구역 실행을 위해 semWait() 함수를 호출한다.
11. 세마포어가 0이므로 P3 프로세스는 Blocked 상태가 되어 큐에 들어간다.
12. P3 프로세스는 Blocked 상태가 되어 큐에 들어간다.
13. P2가 임계구역 실행을 마치고 semSignal() 함수를 호출한다.
14. semSignal() 함수는 Blocked 상태의 프로세스 P3을 깨운다.
* 여기서도 세마포어 변수는 1로 증가하지 않는다.
15. P3가 임계구역을 실행한다.
16. P3가 임계구역 실행을 마치고 semSignal() 함수를 호출한다.
17. Blocked 상태의 프로세스가 없으므로 semSignal() 함수는 세마포어 변수의 값을 1로 변경한다.
이진 세마포어는 세마포어 값을 통해
임계 영역에 진입할 수 있는지 여부를 나타내기 때문에 공유 자원의 인스턴스가 여러 개일 때 효율적이다.
이진 세마포어는 임계 영역에 진입할 수 있는 프로세스의 개수를 관리하지 않기 때문에,
Blocked 상태의 즉 대기 중인 프로세스는 항상 0 또는 1개라고 생각할 수 있다는 점이 흥미롭다.
세마포어의 장점
세마포어를 사용하면 여러 개의 프로세스가 busy waiting을 하지 않아도 된다.
세마포어의 분류 2 - 프로세스가 깨어나는 순서
Blocked 상태의 프로세스는 큐로 관리된다.
큐에 프로세스가 여러 개 담겨있을 경우, 어떤 순서로 프로세스들이 깨어날까?
세마포어는 큐에 담긴 프로세스가 깨어나는 순서에 따라 구분되기도 한다.
강성 세마포어 | 약성 세마포어 | |
깨어나는 순서 | 선입선출 | 순서 명시된 바 없음 |
기아 발생 가능 | X | O |
강성 세마포어
강성 세마포어는 큐에 있는 Blocked, 즉 대기 중인 프로세스를 '오래 대기한 순서대로' 깨어나게 한다.
즉, 선입선출로 큐에 있는 프로세스를 관리한다는 의미이다.
따라서 강성 세마포어의 경우 기아가 발생할 가능성이 없다.
약성 세마포어
약성 세마포어는 큐에 있는 대기 중인 프로세스를 깨어나게 하는 순서를 명시하지 않는다.
따라서 약성 세마포어를 사용할 경우 특정 프로세스가 계속 대기해야 하는 기아가 발생할 가능성이 있다.
뮤텍스
뮤텍스는 이진 세마포어와 관련있는 개념으로, Mutual Exclusion lock의 약자 Mutex로도 표기된다.
뮤텍스는 객체를 얻거나 반납할 때 사용하는 프로그래밍 플래그로,
사용하려는 데이터가 공유될 수 없거나 연산이 동시에 수행될 수 없는 경우 설정된다.
뮤텍스는 값을 0으로 변경해 락을 설정하고, 값을 1로 변경해 락을 해제한다.
임계 영역에 단 하나의 프로세스만 들어갈 수 있다.
세마포어와 뮤텍스의 차이점
세마포어는 값의 범위에 따라 범용 세마포어와 이진 세마포어로 분류된다고 했다.
여기서 세마포어와 뮤텍스의 차이점을 살펴볼 때에는 범용 세마포어와 뮤텍스의 차이점보다 이진 세마포어와 뮤텍스의 차이에 집중하겠다.
그 이유는 이진 세마포어와 뮤텍스가 가질 수 있는 값의 범위가 동일하기 때문이다. (0 또는 1)
이진 세마포어와 뮤텍스의 차이
이진 세마포어 | 뮤텍스 | |
(1) 임계영역 진입 가능 프로세스의 개수 | 공유 자원 인스턴스의 개수만큼 진입 가능 | 1개 |
(2) 락 설정 및 해제 프로세스 | 락 설정한 프로세스 ≠ 락 해제 프로세스 | 락 설정한 프로세스 = 락 해제 프로세스 |
차이 (1)은 이진 세마포어와 뮤텍스만의 차이점이 아니다.
범용 세마포어 또한 공유 자원 인스턴스 개수만큼의 프로세스를 임계영역에 진입하도록 관리할 수 있기 때문이다.
그래서 차이 (2)가 중요하게 다뤄진다.
이진 세마포어의 경우 값을 0 또는 1로 변경하기 위해 semWaitB() 또는 semSignalB() 연산을 사용한다.
semWaitB()를 호출하여 세마포어의 값을 0으로 변경한 프로세스가 semSignalB()를 호출한다고 해서,
세마포어의 값이 1로 변경되지는 않는다는 의미이다.
(semSignalB()를 호출할 당시 Blocked 상태의 프로세스가 있다면 세마포어의 값이 1로 변경되지 않을 것이기 때문이다.)
다시 정리하면 세마포어의 경우 락을 설정한 프로세스와 락을 해제한 프로세스가 다를 수 있다는 말이 된다.
반면 뮤텍스의 경우 값을 0 또는 1로 변경하는 프로세스는,
임계 영역에 들어가기 전 뮤텍스를 0으로 설정하고, 임게 영역 실행 후 1로 설정한다.
따라서 뮤텍스의 경우 락을 설정한 프로세스와 락을 해제한 프로세스가 같을 수 밖에 없다.
마치며
세마포어는 뭔가 어렵다고 생각했다.
하지만 세마포어를 조작하기 위한 연산부터 하나씩 천천히 내용을 따라오다보니 어렵지 않다는 것을 알게 됐다.
이진 세마포어와 뮤텍스의 차이를 논하는 이유부터 그 차이까지,
알기만 하는 것이 아니라 왜 그런지까지 알 수 있었다.
무엇이든지 처음부터 샅샅이 알고 싶어하는 내 성향이
빠르게 배우는 데에는 도움이 되지 않는 듯하여 고민하고 있었는데,
천천히 가는 것이 빠르게 가는 것임을 알게 되었다.
앞으로는, 특히 이론을 익힐 때에는 지금처럼 Bottom Up 방식을 써도 좋겠다!