오늘은 세마포어와 뮤텍스의 개념과 차이점에 대해 알아보겠습니다.
잘못된 내용이 있으면 댓글로 피드백 주시면 정말 감사하겠습니다.
세마포어와 뮤텍스는
"여러 프로세스나 쓰레드가 공유 자원에 접근하는 것을 제어하기 위한 방법"
으로 정의할 수 있습니다. 즉, 병행 처리를 위한 프로세스 동기화 기법입니다.
예를 들자면 교차하는 철도에서 열차의 진행가능 여부를 나타내는 신호등이 세마포어나 뮤텍스가 될 수 있습니다. (둘의 차이는 뒤에서 알아보겠습니다)
세마포어의 동작 원리를 알려면 세마포어 변수, semWait 연산, semSignal 연산에 대해 먼저 알아야 합니다.
세마포어는 정수 값을 가지는 변수로 볼 수 있습니다. 그 정수 값은 접근할 수 있는 최대 허용치 만큼 동시에 사용자 접근을 할 수 있게 합니다.
semWait 연산: 세마포어 값을 감소시킵니다. 만일 값이 음수가 되면 semWait를 호출한 프로세스는 블록됩니다. 음수가 아니면, 프로세스는 계속 수행될 수 있습니다.
semSignal 연산: 세마포어 값을 증가시킵니다. 만약 값이 양수가 아니면(0이거나 음수면), semWait 연산에 의해 블록된 프로세스들을 깨웁니다.
struct semaphore {
int count;
queueType queue;
};
void semWait (semaphore s) {
s.count--;
if (s.count < 0) {
/* 이 구역으로 들어왔다는 것은 현재 프로세스(혹은 쓰레드)가 공유 자원에 접근할 수 없다는 것을 의미*/
/* 요청한 프로세스를 s.queue에 연결 */
/* 요청한 프로세스를 블록 상태로 전이 시킴*/
}
}
void semSignal (semaphore s) {
s.count++;
if (s.count <= 0) {
/* count가 0보다 작거나 같다는 것은 대기하고 있는 프로세스(또는 스레드)가 존재한다는 것을 의미*/
/* s.queue에 연결되어 있는 프로세스를 큐에서 제거 */
/* 프로세스의 상태를 실행 가능으로 전이시키고 ready list에 연결 */
}
}
세마포어는 위와 같이 되어 있고, 이것을 활용하면 다음과 같이 구현할 수 있습니다.
const int n = /* 프로세스 개수 */;
semaphore s = 1;
void P (int i) {
while (true) {
semWait(s);
/* 임계 영역(Critical Section)*/
semSignal(s);
/* 임계 영역 이후 코드 */
}
}
void main() {
parbegin (P(1), P(2), ..., P(n));
}
세마포어는 유지할 수 있는 값의 범위에 따라 이진 세마포어와 범용 세마포어(또는 카운팅 세마포어)로 구분됩니다.
위에서 봤던 세마포어를 카운팅 세마포어(counting semaphore) 또는 범용 세마포어(general semaphore) 라고 부릅니다. 세마포어의 초기값이 0이상의 수입니다.
이진 세마포어(binary semaphore) 는 세마포어의 초기 값이 0또는 1만 가질 수 있는 세마포어 입니다. (이후에 알아볼 것이지만 뮤텍스가 이진 세마포어와 비슷한 것이라 생각하고 계시면 됩니다)
이렇게 세마포어의 초기 값에 따라 범용 세마포어, 이진 세마포어로 나뉘는 것을 공부했습니다.
<참고>
세마포어는 큐에 프로세스들이 여러 개 있을 때 어떤 프로세스부터 깨울지에 따라 강성 세마포어와 약성 세마포어로 나뉩니다. 큐에서 선입선출로 꺼낸다면 강성 세마포어이며, 프로세스들이 큐에서 제거되는 순서를 특별히 명시하지 않은 세마포어를 약성 세마포어라고 합니다.
뮤텍스는 세마포어와 마찬가지로 병행 처리를 위한 동기화 기법 중 하나입니다. 이진 세마포어와 같이 초기값을 1과 0으로 가집니다.
임계영역에 들어갈 때 락(lock)
을 걸어 다른 프로세스(혹은 쓰레드)가 접근하지 못하도록 하고, 임계영역에서 나와 해당 락을 해제(unlock
) 합니다.
간단히 코드로 보자면 다음과 같습니다.
mutex = 1;
void lock () {
while (mutex != 1) {
/* mutex 값이 1이 될 때까지 기다립니다.*/
}
/* 이 구역에 도착했다는 것은 mutex 값이 1이라는 것입니다.
따라서 이제 뮤텍스 값을 0으로 만들어 다른 프로세스(혹은 쓰레드)가 접근하지 못하도록 막아야 합니다.
*/
mutex = 0;
}
void unlock() {
/* 임계 구역에서 나온 프로세스는 다른 프로세스가 접근할 수 있도록 락을 해제합니다.*/
mutex = 1;
}
세마포어는 공유 자원에 세마포어의 변수만큼의 프로세스(또는 쓰레드)가 접근할 수 있습니다. 반면에 뮤텍스는 오직 1개만의 프로세스(또는 쓰레드)만 접근할 수 있습니다.
현재 수행중인 프로세스가 아닌 다른 프로세스가 세마포어를 해제할 수 있습니다. 하지만 뮤텍스는 락(lock)을 획득한 프로세스가 반드시 그 락을 해제해야 합니다.
참고 자료: 도서 <운영체제 내부구조 및 설계 원리>
명쾌한 요약 감사합니다.