운영체제 6-3. 뮤텍스, 세마포어, 모니터

Jang990·2023년 12월 27일
0

운영체제

목록 보기
6/9

6-1에서 동기화를 다루는 임계구역 문제를 알아봤고, 피터슨 알고리즘을 통해 소프트웨어 해결책의 한계를 알아봤다.
6-2에서는 이 한계를 해결하기 위한 하드웨어의 지원을 알아봤다.

하지만 6-2에서 언급한 TAS와 CAS는 응용 프로그래머가 직접 쓰기 힘들다.
이번 글에서 TAS와 CAS를 쓰기 쉽게 만든 소프트웨어 도구인 뮤텍스, 세마포어, 모니터를 다룬다.

이 글에서 설명하는 소프트웨어 도구들은 임계구역 문제를 해결하기 위한 3가지 요구사항 중 상호배제에 초점을 두고 있다.
이 도구들을 적용시켜도 데드락, 기아 문제가 발생할 수 있다는 의미이다.

기아 문제는 CPU 스케줄링과 비슷한 아이디어를 통해 해결할 수 있을 것이다.
하지만 데드락의 경우 좀 복잡하다. 이 내용은 8장에서 다룰 것이다.

락 (Lock)

먼저 소프트웨어 도구들에 대해 살펴보기 전에 용어를 하나만 정리하자.
집을 들어갈 때 열쇠를 통해 들어가는 것처럼
락(Lock)은 임계구역에 들어가기 위한 키로 생각하면 된다.

뮤텍스(Mutex)

뮤텍스는 상호배제를 만족하는 가장 간단한 소프트웨어 도구로
Mutual Exclusion(상호배제)를 축약한 단어이다.
공유 자원에 대한 프로세스(스레드)의 접근을 1개로 제한할 때 사용하기 좋다.

사용 함수 - acquire, release

뮤텍스는 available이라는 하나의 boolean 타입의 락을 가지고,
acquire()를 통해 락을 획득하고 release()를 통해 락을 반환하는 방식이다.
즉 하나의 락을 사용한다.

이 함수는 모두 CAS를 통해 구현할 수 있지만 쉬운 이해를 위해 간단한 코드로 풀어쓴 것이다.
그러니 acquire(), release() 모두 동기화에 대한 문제없이 실행된다고 생각하자.

문제 해결 코드

이제 뮤텍스를 활용해서 임계구역 문제를 해결하면 다음과 같다.
이 코드는 상호배제를 만족하여 동기화 문제를 해결한다.

바쁜 대기(Busy Wait)

뮤텍스 락의 acquire()release()를 다시 살펴보자.
acquire()while문을 통해 availablefalse가 되길 기다리고 있다.
acquire()에서 반복문을 돌며 블로킹된다

여러 프로세스가 실행중 한 프로세스가 임계구역에 진입하여 코드를 실행중일 때 다른 프로세스들은 반복문을 반복하며 대기해야 한다.
이렇게 반복문을 돌며 락을 획득하길 기다리는 것을 바쁜대기(Busy Wait)이라고 부른다.

스핀락

뮤텍스와 같이 바쁜대기(Busy Wait)를 기반으로 구현한 락 유형을 스핀락이라 부른다.
락을 사용할 때까지 프로세스가 회전하기 때문이다.

아이디어: Busy Wait 피하기

반복문을 계속 돌면서 락을 획득할 때까지 기다리는 방식은 비효율적인 것 같다고 생각할 수 있다.
반복문을 돌지 않고 락을 획득할 때까지 대기하려면 어떻게 해야할까?
프로세스를 대기 상태로 돌리고 락을 획득할 수 있을 때 실행해주면 될 것 같다.

이 아이디어는 세마포어에서 이어진다.

스핀락은 무조건 나쁠까?

무조건 나쁘지는 않다.
멀티 코어 환경에서 특정 프로세스가 스핀락을 통해 대기하는 상황과
대기 상태로 전환된 상황을 비교해보자.

2개의 코어가 있는 상황에서
1번 코어에서 동작하는 P1이 락 획득했고,
2번 코어에서 동작하는 P2가 뒤늦게 락을 획득하려는 상황이다.

왼쪽은 P2가 락을 획득하기 위해 스핀락을 도는 예시이고
오른쪽은 P2가 스핀락을 피하기 위해 대기상태로 전환한 예시이다.

예시 1 : P2의 스핀락예시 2 : P2의 대기전환
락을 획득하지 못함락을 획득하지 못함
반복문 돌림대기 상태로 전환
컨텍스트 스위칭
P1이 락반환P1이 락반환
P2가 바로 락 획득P2가 레디 상태로 깨어남
임계구역 진입실행 상태로 전환
컨텍스트 스위칭
빠르게 작업 시작P2가 락 획득
작업...임계구역 진입
작업...스핀락보다 느리게 작업을 시작

스핀락을 사용하지 않고 대기 상태로 전환되는 경우에
컨텍스트 스위칭으로 인해 빠르게 임계구역에 진입하지 못했다.

예시로 든 상황에서는 P1이 락을 빨리 반환했기 때문에 스핀락을 통해 반복문을 돌던 P2가 빠르게 임계구역에 진입할 수 있었다.
(스핀락은 많은 운영체제에서 실제 사용하고 있다.)

하지만 락을 오랜 시간 유지하는 경우 대기 상태로 전환하는 것이 더 좋다.

스핀락을 사용하는 경우
락이 유지되는 시간 < 컨텍스트 스위칭 2번 하는 시간

세마포어

세마포어는 뮤텍스에서 발전된 형태이다.
공유 자원에 대한 프로세스(스레드)의 접근을 여러개로 제한할 때 사용하기 좋다.
세마포어는 다음과 같은 구조를 사용한다.

뮤텍스에서 사용하던 availableint 타입의 value로 바뀌었고,
프로세스 list가 추가됐다.
즉 true와 false만 가능했던 뮤텍스와 다르게 정수형 int 타입을 사용하여 여러 스레드가 임계구역에 진입할 수 있도록 만들었다.

Busy Wait 피하기 항목에서 언급된 아이디어를 다시 생각해보자.
효율적으로 대기하기 위해 프로세스를 대기상태로 돌린다고 했다.
이때 대기하는 프로세스의 정보를 담기위해 list를 사용한다.

사용 함수 - wait, signal

세마포어는 wait()signal() 함수를 사용한다.
wait()에서는 value--를 시키고 만약 세마포어의 value가 0보다 작다면 list에 추가하고 프로세스를 대기 상태로 전환시킨다.
signal()value++을 시키고 만약 세마포어의 value가 0보다 크다면 list의 임의의 프로세스를 깨운다. (깨우고 싶은 임의의 프로세스를 선택하는 과정은 달라질 수 있다. FIFO 등등)


Mutex와 마찬가지로 임계구역 밖에 wait()signal() 함수를 적절하게 사용하면 임계구역 문제를 해결할 수 있다.
뮤텍스와 유사하지만 세마포어는 value 값을 조절하여 1개 뿐만 아니라 여러개의 프로세스(스레드)가 임계구역에 진입하게 만들 수 있다.

뮤텍스와 같이 1개의 프로세스만 허용하는 세마포어를 이진 세마포어라 부르고,
여러개의 프로세스를 허용하는 세마포어는 카운팅 세마포어라 부른다.

여전히 존재하는 Busy Wait

다시 wait()signal()의 코드를 살펴보자.
wait()의 코드와 signal() 내부의 코드는 결국 전부 원자적으로 실행되어야 한다.
결국 wait()signal()도 임계구역이라는 의미이다.
여러 프로세스가 세마포어의 value를 동시에 ++시키거나 동시에 list에 프로세스를 넣거나 제거하면 동기화 문제가 발생한다.

결국 다시 바쁜 대기(Busy Wait)를 해야 한다.

결국 Busy Wait?

Busy Wait 피하기 항목에서 언급됐듯 뮤텍스의 Busy Wait를 피하기 위해 세마포어에서는 프로세스를 대기 상태로 전환했다.
그럼에도 불구하고 세마포어에서는 Busy Wait가 발생했다.
결국 Busy Wait을 피하지 못한것 아닌가?

아니다. 이 둘은 범위에서 차이가 난다.

뮤텍스는 락 획득을 시도할 때 임계영역에 들어간 프로세스가 락을 반환하길 기다렸다.

세마포어는 락 획득을 시도할 때 wait() 함수에 대한 락을 획득하고 대기상태로 들어간다.

wait(), signal() 내부의 명령어는 길어도 10줄을 넘지 않기 때문에
바쁜 대기는 자주 발생하지 않는다.

이렇게 세마포어는 Busy Wait을 완전히 제거하지 못했지만, 범위를 wait(), signal()로 줄였다는 것을 알아야 한다.

타이밍 에러

앞서 2가지의 소프트웨어 도구로도 충분히 상호배제를 만족하며 동기화 문제를 해결할 수 있다.

하지만 여전히 사용하기 까다롭다.
항상 임계구역에 진입하기 전에는 wait()을 호출해야하고 임계구역에서 나올 때 signal()을 호출해야 한다는 규칙을 지켜야 한다.

  • 만약 signal()을 먼저 호출한다면?
  • siganl()wait()의 위치가 뒤바뀌였다면?

프로그래머가 살짝만 잘못해도 에러가 발생한다.

모니터

추후 추가 예정

참고 및 출처

도서 - 운영체제
인프런 - 운영체제 공룡책 강의
https://dev.to/rinsama77/process-synchronization-with-busy-waiting-4gho

profile
공부한 내용을 적지 말고 이해한 내용을 설명하자

0개의 댓글