반응형
더보기
목차
- Race Condition
- Critical Section
- Synchronization Instruction
- Semaphores
- Deadlock
- Monitor
Race Condition
- 정의
- 여러 프로세스가 동시에 공유 데이터에 접근하여 일관성(Consistency)이 깨질 수 있는 상황
- 공유 데이터:
- 여러 프로세스가 동시에 접근하여 데이터를 읽거나 쓰는 자원.
- Race Condition이 발생하면 데이터가 예기치 않게 변경될 수 있음.
- 문제 해결 필요성
- 데이터 일관성을 유지하려면 프로세스 간 동기화가 필요
- 특히 협력(Cooperating) 하는 프로세스들이 공유 자원을 안전하게 사용하도록 순차적 실행이 보장되어야 함
- 예제
- 1000원의 balance 가 남아있을 때, 500원의 입금과 500원의 출금이 동시에 일어날 경우, 그 결과는?
- 두 연산이 동시에 처리될 경우, 최종 잔액이 예상치와 다르게 계산될 수 있음
- 컴파일 하기 전의 소스코드
- 1000원의 balance 가 남아있을 때, 500원의 입금과 500원의 출금이 동시에 일어날 경우, 그 결과는?
- 컴파일러의 최적화
- 컴파일러는 Balance 값을 메모리에서 읽고 변경 후 다시 메모리에 기록하는 식으로 최적화.
- 코드가 분할되어 순차적으로 처리되지 않을 가능성이 있음:
- 프로세스 A와 B가 동시에 실행되면 중간값이 잘못된 결과를 초래.
- 결과
- 최종적으로 잔액이 500원 또는 1500원이 되는 불일치가 발생.
Interleaved Execution
- 프로세스 A와 B의 실행 흐름
- 프로세스 A: 현재 계좌 잔액(Balance)을 읽고 500을 더함.
- 프로세스 B: 같은 잔액을 읽고 500을 뺌.
- 결과
- 두 프로세스가 동시에 실행되면 순서에 따라 결과가 다를 수 있음.
- 최종 값이 잘못 계산될 가능성이 큼 → 데이터의 일관성 손실 발생.
Critical Section
- 정의
- 여러 프로세스가 공유 자원에 접근할 때 동시에 실행되면 문제가 생기는 코드 영역.
ex) 은행 계좌의 잔액(Balance)을 읽고 쓰는 코드.
- 여러 프로세스가 공유 자원에 접근할 때 동시에 실행되면 문제가 생기는 코드 영역.
- 구조
- Entry Section: 프로세스가 Critical Section에 진입하기 전에 동기화를 통해 실행 순서를 조정.
- Critical Section: 공유 자원에 접근하여 작업을 수행하는 코드 영역.
- Exit Section: 작업이 완료된 후 Critical Section을 나가는 코드.
- Remainder Section: Critical Section 외의 나머지 작업.
Critical Section 해결의 조건들
Critical Section 문제를 해결하려면 다음 세 가지 조건을 만족하는 알고리즘을 사용해야 한다.
- Mutual Exclution (상호 배제)
- 한번에 하나의 프로세스만 Critical Section 에 진입 가능
- 다른 프로세스는 현재 프로세스가 Critical Section 에서 나올 때까지 대기해야 함
- Progress (진행)
- Critical Section 에 프로세스가 없으면, 진입을 대기 중인 프로세스 중 하나가 진입 가능해야 함
- 무한 대기 방지
- Bounded Waiting (한정된 대기)
- 프로세스가 Critical Section 에 진입하기까지의 대기 시간이 제한되어야 함
- 특정 프로세스가 무한정 대기하지 않도록 보장
두 Process 를 위한 Algorithm
- Shared Variables
- int turn : 두 프로세스가 번갈아가며 Critical Section 에 진입하도록 조정하는 변수
- turn = 0 일 때,
- Mutual Exclusion (O)
- Progress, Bounded Waiting (X)
다른 Algorithm
- Shared Variables
- boolean flag[2]; flag[0] = flag[1] = false 로 초기화
- flag[0] = true 일 때, $P_{0}$ 이 critical section에 진입 가능
- flag[1] = true 일 때, $P_{1}$ 이 critical section에 진입 가능
- Mutual Exclusion (O)
- Progress, Bounded Waiting (X)
- $P_{0}$ 실행, flag[0] = true, flag[1] = false
- context switching $P_{1}$ 실행, flag[0] = true, flag[1] = true
- $P_{1}$ 무한대기
- $P_{0}$ 무한대기
Peterson Solution
- Shared Variables
- int turn;
- boolean flag[2]; flag[0] = flag[1] = false 로 초기화
- Mutual Exclusion, Progress, Bounded Waiting (O)
- $P_{0}$ 실행, flag[0] = true, flag[1] = false
- context switching, $P_{1}$ 실행, flag[0] = true, flag[1] = true
- turn = 0; $P_{1}$ 무한 대기
- $P_{0}$ 실행, turn = 1, $P_{0}$ 무한 대기
- context switching $P_{1}$ critical section 진입 ...
- Mutual Exclusion, Progress, Bounded Waiting (O)
Peterson Solution의 한계
- 세 개 이상의 프로세스가 참여할 경우 확장이 어려움
- 확장된 Algorithm 의 증명도 NP 문제
- SW 로 해결하는 대신 하드웨어로 처리하면 알고리즘이 매우 간단하게 됨
간단한 방법
- Critical Section 에 들어가면서 Interrupt 를 Disable 한다
- Interrupt 를 비활성화하면, 다른 프로세스가 CPU 를 차지할 수 없으므로 Critical Section 에 안전하게 접근 가능
- User Program 이 Interrupt 를 Control 하는 것은 바람직하지 않음
- Scalable 하지 않음
- process 의 숫자가 많아 질 때 성능 저하 및 문제가 생길 수 있음
- 동기화를 위한 Instruction 이 도입됨
Synchronization Instruction
- CPU 에서 지원하여 원자적 (Atomically) 으로 수행되는 명령어(Instruction) 이용
- 명령어가 수행되는 동안 방해 받지 않음(= Uninterruptible)
- 명령어가 수행되는 동안 방해 받지 않음(= Uninterruptible)
1. Test-and-Set
- 메모리에서 값을 읽고(Test), 동시에 값을 변경(Set)
- 실행 도중 다른 프로세스의 개입을 허용하지 않음(interrupt 불가)
boolean TestAndSet(boolean *target) {
boolean rv = *target;
*target = true;
return rv;
}
- 변수
- rv : 이전 값 (현재 잠금 상태) 을 반환
- *target = true : 잠금을 설정
- 동작
- lock = false
- lock = true
- rv = false, TestAndSet(&lock) = rv = false 이므로 critical section 에 진입
- lock = false 가 실행되기 전까지는 lock = true 이므로 잠긴다.
- lock = false 가 실행되면, 다른 Process 가 진행이 가능하다.
2. Swap
- Swap 명령어는 두 변수의 값을 교환하는 원자적(Atomic) 연산
- 이 명령어는 하드웨어적으로 동작하며, 두 변수의 값을 서로 바꾸는 동작을 중단 없이 수행
- Shared Variables
- boolean lock
- boolean waiting[n]
void Swap(boolean *a, boolean *b) {
boolean temp = *a;
*a = *b;
*b = temp;
}
- 입력: 두 변수(*a, *b).
- 동작:
- 임시 변수(temp)에 *a 값을 저장.
- *a에 *b 값을 복사.
- *b에 temp 값을 복사하여 두 값 교환.
- 특징:
- Swap 연산은 원자적으로 실행되며, 도중에 중단되지 않음.
Process Pi:
do {
waiting[i] = true; // 자신이 대기 상태임을 표시
while (waiting[i] == true) // 다른 프로세스가 끝날 때까지 대기
swap(&lock, &waiting[i]); // Swap 연산으로 Critical Section 접근 시도
// Critical Section
lock = false; // Critical Section 종료 후 잠금 해제
// Remainder Section
} while (true);
- $P_{1}$ 실행, waiting[1] = true
- while 문 진입, swap 호출
- lock = false, waiting[1] = true 교환
- lock = true, waiting[1] = false
- while 문 종료
- $P_{1}$ critical section 진입
- context swtiching $P_{2}$ 실행, waiting[2] = true, lock = true
- while 문 진입, swap 호출
- lock = true, waiting[2] = true, while 문 무한루프
- $P_{1}$ context switching 되고 lock= false 실행하고, 이후엔 $P_{2}$ 이 critical section 진입 가능
위와 같은 Instruction 의 한계점
- 동기화 Instruction 을 쓰면, Mutual Exclusion 은 해결, Bounded Waiting 같은 조건은 User Program 에서 제공해야함
- Bounded Waiting 이 주어진 문제마다 조금씩 차이가 있기 때문, User Program 에서 제대로 처리하는 것을 기대하기 어려움
- 좀 더 Comprehensive 한 동기화 Primitive 가 필요함
Semaphore
- 정의
- 동기화 문제를 해결하기 위한 정수 변수
- 두 개의 원자적 연산을 통해 Critical Section 에 대한 접근을 제어
- P() 또는 Wait() : 자원을 사용하기 위해 대기
- Critical Section 들어가기 전에 수행
- V() 또는 Signal() : 자원을 반환하여 다른 프로세스가 사용 가능하도록 허용
- Critical Section 나와서 수행
- P() 또는 Wait() : 자원을 사용하기 위해 대기
- 특징
- 원자적으로 수행됨
- 한 번에 하나의 프로세스만 연산을 수행 가능
- 하나의 Process 가 P 를 수행하여 세마포어의 값을 수정하는 동안, 다른 Process 에서 P나 V 를 수행하여 같은 세마포어의 값을 수정하지 못함
- 세마포어 값(S) 는 공유 자원의 상태를 나타냄
- S > 0 : 사용 가능한 자원이 존재
- S <= 0 : 자원이 사용 중이며, 대기 중인 프로세스가 있음
- 원자적으로 수행됨
Semaphores (세마포어) 의 구현
1. Busy Waiting
P(S)
while (S <= 0); // 조건을 만족할 때까지 반복
S = S - 1; // 자원 감소
V(S)
S = S + 1;
- 동작
- S = 1 : Critical Section 이 비어있음
- $P_{1}$ 실행, P() : S = S - 1
- S= 0, $P_{1}$ 이 Critical Section 진입
- Context Switching $P_{2}$ 실행, S=0, 무한대기 (Busy Waiting)
- Context Switching $P_{1}$ 실행, V() : S = S+1, S= 1
- Context Switching $P_{2}$ 실행, 자원이 남아있으므로, Critical Section 진입 가
- 단점
- CPU 소모:
- Busy Waiting 방식에서는 while 루프를 통해 CPU가 지속적으로 조건을 검사.
- Critical Section에 진입 가능한 상태가 될 때까지 반복하므로 CPU 자원이 낭비됨.
- 누가 진입할지 결정하지 않음:
- 대기 Process 중 누가 Critical Section 에 진입할지 결정하지 않음
- CPU 소모:
2. Sleep Queue
- Busy Waiting 의 문제 해결
- Sleep Queue 를 사용하여 대기 중인 프로세스를 CPU 에서 제외하고 Sleep 상태로 전환
- Critical Section 이 비게 되면 (S > 0) Sleep Queue 에서 깨워 실행 (자료구조 필요)
- 자료구조
typedef struct {
int value; // 세마포어 값
struct process *list; // Sleep Queue (대기 중인 프로세스 목록)
} semaphore;
P(semaphore *S)
if (S->value <= 0) {
add this process to S->list; // 대기 중인 프로세스를 Sleep Queue에 추가
block(); // 프로세스를 Sleep 상태로 전환
}
S->value--; // 자원 감소
V(semaphore *S)
S->value++; // 자원 증가
if (S->value > 0) {
remove a process from S->list; // Sleep Queue에서 대기 프로세스 제거
wakeup(p); // 제거된 프로세스를 실행 상태로 전환
}
- 변수
- value:
- 자원 사용 가능 여부를 나타냄.
- value > 0: 사용 가능한 자원이 존재.
- value <= 0: 자원이 모두 사용 중이며 대기 프로세스가 존재.
- list:
- 대기 중인 프로세스를 관리하는 Sleep Queue.
- value:
- 장점
- CPU 효율성: 대기 프로세스는 Sleep 상태로 전환되어 CPU 자원을 사용하지 않음.
- 공정성 보장: Sleep Queue에 추가된 프로세스는 FIFO 방식으로 관리되어 공정성이 보장됨.
- 확장성: 다중 프로세스 환경에서도 효율적으로 동작.
Semaphores (세마포어) 의 종류
- Counting Semaphore
- 정의:
- 자원의 개수가 여러 개일 때 사용. (범위가 정해져 있지 않음)
- 초기 값은 가능한 자원의 수로 정해짐
- 특징: 자원의 수가 동적으로 변화 가능.
ex) 데이터베이스 연결, 프린터 큐 등.
- 정의:
- Binary Semaphore
-
- 정의:
- S = 1: Critical Section이 비어 있음.
- S = 0: Critical Section이 사용 중.
- 특징:
- Counting Semaphore 보다 구현이 간단함
- 이를 이용하여 Counting Semaphore 를 구현할 수 있음
- Test-and-Set 과 같은 Hardware 의 도움을 받아서 구현할 수 있음
- 정의:
-
Binary Semaphores (세마포어) 의 구현
- Test-and-Set 명령어를 이용하여 구현
- Semaphore S : 현재 Critical Section 에 진입한 Process 가 있는지 나타냄 (True or False)
- P(S) : while(test_and_set(&S))
- S 값이 return 되고, S 는 True 로 바뀜
- V(S) : S = false
- 진입한 Process 가 없음을 나타내어 wait () 에서 대기중인 Process 를 진입 가능하도록 함
- P(S) : while(test_and_set(&S))
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
// Test-and-Set 함수
boolean TestAndSet(boolean *target) {
boolean old_value = *target; // 이전 값을 저장
*target = true; // target 값을 true로 설정
return old_value; // 이전 값을 반환
}
// Binary Semaphore 구조
typedef struct {
boolean lock; // Critical Section 상태 (true = 사용 중, false = 비어 있음)
} BinarySemaphore;
// P(S) 연산 (Wait)
void P(BinarySemaphore *S) {
while (TestAndSet(&S->lock)) {
// Busy Waiting: lock이 해제될 때까지 반복
}
}
// V(S) 연산 (Signal)
void V(BinarySemaphore *S) {
S->lock = false; // Critical Section 해제
}
// Process 예제
void Process1(BinarySemaphore *S) {
P(S); // Critical Section 진입 시도
printf("Process 1: Critical Section Start\n");
sleep(1); // Critical Section 작업
printf("Process 1: Critical Section End\n");
V(S); // Critical Section 해제
}
void Process2(BinarySemaphore *S) {
P(S); // Critical Section 진입 시도
printf("Process 2: Critical Section Start\n");
sleep(1); // Critical Section 작업
printf("Process 2: Critical Section End\n");
V(S); // Critical Section 해제
}
int main() {
BinarySemaphore S = {false}; // 초기 상태: Critical Section이 비어 있음
if (fork() == 0) {
Process1(&S); // 자식 프로세스 실행
} else {
Process2(&S); // 부모 프로세스 실행
}
return 0;
}
- 동작
- 초기 상태
- lock = false : Critical Section 이 비어 있음
- $P_{1}$ 실행
- P(S) 호출
- Test_and_Set (&S->lock) 에서 lock = false
- lock = true 로 설정되고 false 리턴, critical section 진입
- P(S) 호출
- Context Switching $P_{2}$ 실행
- P(S) 호출, lock = true
- Test_and_set (&S->loc) 에서 lock = true
- lock = true 로 설정되고 true 리턴, busy waiting
- Context Switching $P_{1}$ 실행
- V(S) 호출
- lock = false 로 설정하여 Critical Section 해제
- V(S) 호출
- Context Switching $P_{2}$ 실행
- Busy waiting 종료, lock = false
- lock = true 설정되고 false 리턴
- Critical Section 진입
- 초기 상태
Counting Semaphores (세마포어) 의 구현
- Counting Semaphore = CS, CS 의 Value = C
- 2개의 Binary Semaphore S1, S2 사용
- S1 = 1, S2 = 0 로 초기화 (C가 양수로 가정)
- S1 : CS 의 자원 수를 제어
- S2 : 대기 Process 관리
- Wait Operation (P(S))
- 자원(C)이 남아 있다면 즉시 Critical Section에 진입.
- 자원이 부족하다면: 대기 상태로 전환.
- Signal Operation (V(S))
- 자원을 반환하며 대기 상태에 있는 프로세스를 깨움.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
// Binary Semaphore를 구조체로 정의
typedef struct {
sem_t sem; // 세마포어
} BinarySemaphore;
// Binary Semaphore 초기화
void initBinarySemaphore(BinarySemaphore *bsem, int value) {
sem_init(&bsem->sem, 0, value);
}
// P(S) 연산 (Wait)
void P(BinarySemaphore *bsem) {
sem_wait(&bsem->sem); // 세마포어 값을 감소, 0이면 대기
}
// V(S) 연산 (Signal)
void V(BinarySemaphore *bsem) {
sem_post(&bsem->sem); // 세마포어 값을 증가, 대기 중인 프로세스를 깨움
}
// Counting Semaphore 구조체 정의
typedef struct {
int C; // 현재 자원의 개수
BinarySemaphore S1; // Binary Semaphore 1
BinarySemaphore S2; // Binary Semaphore 2
} CountingSemaphore;
// Counting Semaphore 초기화
void initCountingSemaphore(CountingSemaphore *csem, int initialValue) {
csem->C = initialValue; // 초기 자원 개수 설정
initBinarySemaphore(&csem->S1, 1); // S1은 1로 초기화
initBinarySemaphore(&csem->S2, 0); // S2는 0으로 초기화
}
// Wait Operation (P(S))
void P_CountingSemaphore(CountingSemaphore *csem) {
P(&csem->S1); // S1을 P(S) 호출
csem->C--; // 자원 개수 감소
if (csem->C < 0) { // 자원이 부족하면
V(&csem->S1); // S1을 해제
P(&csem->S2); // S2에서 대기 (Sleep Queue 역할)
} else {
V(&csem->S1); // S1을 해제
}
}
// Signal Operation (V(S))
void V_CountingSemaphore(CountingSemaphore *csem) {
P(&csem->S1); // S1을 P(S) 호출
csem->C++; // 자원 개수 증가
if (csem->C <= 0) { // 대기 중인 프로세스가 있으면
V(&csem->S2); // S2에서 깨움
}
V(&csem->S1); // S1을 해제
}
// 테스트용 쓰레드 함수
void *process(void *arg) {
CountingSemaphore *csem = (CountingSemaphore *)arg;
printf("Process %ld: Waiting to enter Critical Section...\n", pthread_self());
P_CountingSemaphore(csem); // Critical Section 진입 대기
printf("Process %ld: Entering Critical Section\n", pthread_self());
sleep(1); // Critical Section에서 작업 수행
printf("Process %ld: Leaving Critical Section\n", pthread_self());
V_CountingSemaphore(csem); // Critical Section 해제
return NULL;
}
int main() {
CountingSemaphore csem;
initCountingSemaphore(&csem, 2); // 초기 자원 수 = 2 (동시에 2개 프로세스만 진입 가능)
pthread_t threads[5];
for (int i = 0; i < 5; i++) {
pthread_create(&threads[i], NULL, process, &csem);
}
for (int i = 0; i < 5; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
Process 12345: Waiting to enter Critical Section...
Process 12345: Entering Critical Section
Process 12346: Waiting to enter Critical Section...
Process 12346: Entering Critical Section
Process 12347: Waiting to enter Critical Section...
Process 12345: Leaving Critical Section
Process 12347: Entering Critical Section
Process 12346: Leaving Critical Section
Process 12347: Leaving Critical Section
- 초기 상태
- C = 1
- Binary Semaphore S1 : 1 (Counting Semaphore 접근 가능)
- Binary Semaphore S2 : 0 (Sleep Queue, 대기 중인 프로세스 없음)
- 동작
- $P_{1}$ 실행, P(S1) 호출
- S1 = 0, C = 0
- V(S1) 호출, S1 = 1
- P(S2) 호출
- $P_{1}$ Critical Section 진입
- Context Switching $P_{2}$ 실행, P(S1) 호출
- S1 = 0, C = -1
- V(S1) 호출, S1 = 1
- P(S2) 호출 : Sleep Queue 에서 대기
- $P_{1}$ 가 Critical Section 을 완료하고 V(S) 호출
- P(S1) 호출, S1 = 0, C=0
- V(S2) 호출, Sleep Queue 에서 대기 중인 $P_{2}$ 깨움
- V(S1) 호출, S1 = 1
Semaphore 의 구현
- Kernel 이 Single Thread 인 경우
- P()와 V() 연산이 System Call로 호출.
- Non-Preemptive Kernel에서 동작:
- Kernel 내에서 세마포어 동작이 보장됨.
- Kernel이 Critical Section을 실행 중일 때는 다른 프로세스가 Kernel에 접근 불가.
- Kernel 이 MultiThread 인 경우
- Kernel이 여러 스레드를 실행하므로 동기화 필요.
- Kernel 내부에서도 별도로 세마포어 또는 동기화 메커니즘을 사용해야 함.
Semaphore 의 단점
- Deadlock 이 발생할 가능성이 존재
- P와 V의 연산이 분리되어 있기 때문에 이를 잘못 사용할 경우에 대한 대책이 없음
- P() -> Critical Section -> P()
- Critical Section 후에 Lock 을 풀어주는 V() 가 없으므로 한 Process가 Critical Section 에 진입하면, 나올 수 없음
(다른 Process 들이 Critical Section 에 진입 못하므로 Deadlock 발생)
- Critical Section 후에 Lock 을 풀어주는 V() 가 없으므로 한 Process가 Critical Section 에 진입하면, 나올 수 없음
- V() -> Critical Section -> P()
- Critical Section 전에 Lock 을 걸어주는 P() 가 없으므로 여러 Process 가 동시에 Critical Section 에 진입할 수 있으므로, Mutual Exclusion 을 보장하지 못함
- P() -> Critical Section -> P()
- High Level 언어에서 동기화를 제공하는 방법이 필요해짐
Deadlock
- 두 개 이상의 프로세스가 서로가 가진 자원을 기다리며, 아무도 작업을 진행하지 못하는 상태.
- 자원(Resource)이 순환 대기(Circular Wait) 상태에 빠지면 발생.
- $P_{1}$ 이 자원 Q 를 점유하고 자원 S 를 요청
- $P_{2}$ 이 자원 S 를 점유하고 자원 Q 를 요청
- $P_{1}$, $P_{2}$ 가 서로를 기다리는 상태가 발생 (Deadlock)
Monitor
- High-Level 언어에서의 동기화 방법
- Java 의 Thread 에서 동기화를 방법으로 사용되는 방식임
- 한 순간에 하나의 Process 만 Monitor 에서 활동하도록 보장
- Application 은 Semaphore 와 같이 P 와 V 연산에 대한 고려 없이 Procedure 를 호출하는 것만으로 동기화를 해결할 수 없음
- Programmer 는 동기화 제약 조건 (P와 V 연산 같은 것)을 명시적으로 코드화할 필요가 없음
- 특징
- Entry Queue:
- Monitor에 진입하려는 프로세스들이 대기하는 큐.
- 하나의 프로세스만 Monitor에 진입 가능.
- Shared Data: Monitor 내부의 공유 데이터는 오직 Monitor의 메서드(Procedure)를 통해서만 접근 가능.
- Synchronization: Monitor는 내부적으로 동기화 문제를 해결하며, 프로세스 간 충돌 방지.
- Entry Queue:
- 문법
- Java 나 C++ 클래스의 구문과 유사
- 공유 변수 (shared variable):
- Monitor 내부에서 사용하는 데이터.
- Procedure:
- Monitor 내부에서 공유 데이터를 사용하는 메서드.
- 프로세스는 Monitor의 Procedure를 호출하여 작업 수행.
- 공유 변수 (shared variable):
- Java 나 C++ 클래스의 구문과 유사
Monitor monitor_name {
shared variable v;
procedure body p1(...) {
// Monitor에서 실행할 코드
}
procedure body p2(...) {
// Monitor에서 실행할 코드
}
{
// 초기화 코드
}
...
}
- 동작 과정
- 프로세스가 Monitor 를 사용하기 위해 Entry Queue 에 진입
- Monitor 가 비어 있으면, 해당 프로세스가 Monitor 내부로 들어가 작업 수행
- 다른 프로세스는 Entry Queue 에서 대기
- 작업이 완료되면 Monitor 를 나가며, 대기 중인 프로세스를 깨움
- 동작 과정
- 진입 대기 큐에서 Monitor로 접근:
- 여러 프로세스가 Monitor에 접근하려고 하면, 하나의 프로세스만 Monitor 내부로 진입
- 나머지 프로세스는 Queue of Entering Processes에서 대기
- Monitor 내부 실행:
- 진입한 프로세스는 필요한 Procedure를 호출하여 작업을 수행
- 작업 중 특정 조건이 충족되지 않으면, Condition Variables(e.g., x.wait)를 사용하여 대기 상태로 들어감
- 조건 대기 상태:
- 조건이 충족되지 않으면, 해당 조건 변수(x.wait)의 대기 큐에 추가됨
- 다른 프로세스가 x.signal을 호출하여 조건을 충족시키면, 대기 중인 프로세스가 깨워짐
- 긴급 대기 큐 처리:
- x.signal로 조건이 충족된 프로세스는 Urgent Queue로 이동
- Urgent Queue는 Monitor 내부에서 가장 높은 우선순위를 가진다
- Monitor 종료:
- 작업이 완료되면 Monitor를 빠져나가며, 대기 중인 프로세스 중 하나를 깨워준다
- Monitor는 다시 다른 프로세스에게 접근을 허용한다
- 진입 대기 큐에서 Monitor로 접근:
- 예시 (Transaction in Database)
- Transaction
- DB 내에 하나의 그룹으로 처리해야 하는 명령문들을 모아놓은 작업 단위
- Transaction 내에 묶인 하나의 작업 단위가 반드시 완전히 수행
- 만약 어느 하나라도 실패한다면 전체 명령문이 취소 됨
- Transaction
반응형
'3학년 2학기 학사 > 운영체제' 카테고리의 다른 글
[운영체제] #10. Memory Management (1) (0) | 2024.12.01 |
---|---|
[운영체제] #9. 동기화(2) (0) | 2024.12.01 |
[운영체제] #7. InterProcess Communication (IPC) (0) | 2024.11.26 |
[운영체제] #5. Computer Architecture (0) | 2024.11.24 |
[운영체제] #4. Process (0) | 2024.11.20 |