안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.
이번 포스팅은 운영체제의 세마포어에 대한 개념을 정리하고자 합니다.
1. 세마포어란
- 프로세스 간에 시그널을 주고받기 위해 사용되는 정수 값으로, 세마포어는 3가지 원자적인 연산을 지원합니다.
initialize, decrement, increment - decrement 연산은 프로세스를 블록 시킬 수 있습니다. 반면 increment 연산은 블록되었던 프로세스를 깨울 수 있습니다.
- 복잡한 프로세스 간 상호작용 속에서 세마포어 s를 통해 시그널을 전송하기 위해 프로세스는 semSignal(s)라는 프리미티브를 수행합니다.
- 반면, 세마포어 s를 통해 시그널을 수신하기 위해 프로세스는 semWait(s)라는 프리미티브를 수행합니다.
- 종류는 범용 세마포어, 이진 세마포어 등이 있습니다.
2. 세마포어 연산
- 세마포어 초기화: 세마포어는 음이 아닌값으로 초기화됩니다.
- semWait 연산: 세마포어 값을 감소시킵니다. 만일 값이 음수가 되면, semWait를 호출한 프로세스는 블록됩니다. 음수가 아니면 프로세스는 계속 수행됩니다.
- semSignal 연산: 세마포어 값을 증가시킵니다. 만약 값이 양수가 아니면 semWait 연산에 의해 블록된 프로세스를 깨웁니다.
3. 세마포어의 사용 예
세마포어는 여러 프로세스 또는 스레드가 공유 리소스에 동시 접근하는 것을 제어하는데 사용됩니다.
- 공유 메모리 : 여러 프로세스가 동시에 메모리에 접근할 때, 서로 읽기 쓰기 작업이 충돌하지 않도록 보장하기 위해 사용됩니다.
- 파일 시스템: 여러 프로세스가 동시에 파일이나 디렉토리에 접근할 때, 데이터 일관성을 유지하기 위해 사용됩니다.
- 프로세스간 통신: 여러 프로세스가 메세지를 전달하거나 데이터를 주고받을 때, 동기화를 위해 사용됩니다.
- 데이터베이스 시스템: 여러 사용자나 애플리케이션에서 동시에 데이터베이스에 접근할 때, 트랜잭션의 원자성과 일관성을 보장하기 위해 사용됩니다.
4. 세마포어 프리미티브
< 범용 세마포어 프리미티브 >
public class OsExample {
class GeneralSemaphore {
int count;
Queue<Pid> queue;
}
class Pid {
public Pid() {}
}
void semWait(GeneralSemaphore s) {
s.count--;
if (s.count < 0) {
/* 요청한 프로세스를 s.queue에 연결 */
/* 요청한 프로세스를 블록 상태로 전이시킴 */
}
}
void semSignal(GeneralSemaphore s) {
s.count++;
if (s.count <= 0) {
/* s.queue에 연결되어 있는 프로세스를 큐에서 제거 */
/* 프로세스 상태를 실행 가능으로 전이시키고 redy list에 연결 */
}
}
}
< 이진 세마포어 프리미티브 >
public class BinarySemaphoreEx {
class BinarySemaphore {
Value value;
Queue<Pid> queue;
}
class Pid {
public Pid() {}
}
void semWaitB(BinarySemaphore s) {
if (s.value == ONE) s.value = ZERO;
else {
/* 요청한 프로세스를 s.queue에 연결 */
/* 요청한 프로세스를 블록 상태로 전이 */
}
}
void semSignalB(BinarySemaphore s) {
if (s.queue.isEmpty()) s.value = ONE;
else {
/* s.queue에서 프로세스 p를 제거 */
/* 프로세스의 p의 상태를 실행 가능으로 전이, ready list에 연결 */
}
}
}
5. 세마포어 정책
범용 세마포어와 이진 세마포어 모두 세마포어에서 블록된 프로세스들을 관리하기 위해 큐를 사용합니다.
세마포어는 큐를 적용하는 정책에 따라 두 가지로 분리할 수 있습니다
강성 세마포어
프로세스들이 세마포어를 사용할 때, 먼저 semWait를 호출한 프로세스가 먼저 semSignal을 호출할 수 있는 순서로 실행됩니다.
즉, 강력한 선입선출을 유지하여 많은 OS에서 강성 세마포어를 주로 사용하고 있습니다.
<강성 세마포어 실행 순서>
- 프로세스 D 실행 s = 1 (S는 D가 실행되면 +1 )
- D는 준비 큐 대기
- A프로세스, B프로세스, D 준비 상태
- A 프로세스가 스케줄링 -> A가 semWait() 호출 -> s 소비 -> s = 0 -> A 실행
- B 프로세스 semWait() 호출 -> s <= 0 이므로 s = -1한 후, B 블록 큐 이동
- A 프로세스 타임아웃으로 준비 큐 대기
- D 프로세스 semSignal() 호출로 s++, 이어서 블록 큐에서 프로세스 B 호출
- B가 실행 후, 종료
- C가 준비 큐에서 스케줄링 -> semWait()를 호출 -> s = 0이므로 s--, C는 블록
- A가 준비 큐에서 스케줄링 -> semWait()를 호출 -> s = -1이므로 s--. A는 블록
- D가 준비 큐에서 스케줄링 -> semSignal()를 호출 -> s++, s-= -1, 블록된 'C' 실행
약성 세마포어
약성 세마포어는 큐를 사용하거나 혹은 다른 매커니즘을 사용할 수 있습니다. 만약 적용되는 프로세스가 실행 순서에 대한 요구사항이 없다면 다른 동기화 매커니즘을 사용하여 실행 순서를 관리할 수 있습니다. 따라서, 약성 세마포어는 '기아' 상태가 발생할 수 있습니다.
6. 상호 배제
세마포어를 사용할 경우, 공유 자원에 접근하려는 n개의 프로세스를 상호 배제할 수 있습니다.
프로세스가 공유 자원을 접근하려는 코드 부분이 임계영역으로 정의되고, 긱 프로세스는 임계영역에 들어가기 전 semWait를 호출합니다. s의 값의 범위에 따라 해당 프로세스의 블록이 결정됩니다.
만약 s가 양수라면 임계영역 내부로 입장하고, 임계 영역에서 나오며 s를 다시 증가킵니다.
따라서, s.count는 다음과 같이 정리할 수 있습니다.
- s.count >= 0 : s.count는 현재 임계영역에 블록됨이 없이 진입할 수 있는 프로세스의 수를 나타냅니다.
- s.count < 0: s.count의 절대값은 s.queue에 블록되어 있는 프로세스의 수를 나타냅니다.
운영체제는 공부해도 시간이 지나면 헷갈리고 잊어버리게 되는 것 같습니다.
꾸준히 정리하며, 공부를 이어나가도록 하겠습니다.
감사합니다!
- 자료 출처 : 운영체제 8판 내부구조 및 설게원리
'OS' 카테고리의 다른 글
[OS] 단일처리기 스케줄링 (0) | 2023.04.25 |
---|---|
[OS] 가상메모리(상) (0) | 2023.04.25 |
[OS] 메모리 관리 (0) | 2023.04.19 |
[OS] 상호 배제를 위한 모니터 (0) | 2023.04.19 |