멀티 프로그래밍에서 공유 데이터에 동시 접근할 경우 데이터에 일관성이 깨지는 문제가 발생할 수 있습니다. 한 스레드에서 공유 데이터를 변경하던 도중 Context Switching이 발생하여 다른 스레드가 잘못된 공유 데이터를 참조하거나, 중복 업데이트가 일어날 수 있습니다. 이러한 문제를 해결하기 위해 동기화라는 개념이 나왔습니다.
동기화 문제를 해결하기 위해서 아래 세 가지 조건을 모두 만족해야합니다.
세마포어는 동시에 공유 데이터에 접근할 수 있는 프로세스의 갯수를 제어합니다. 예를 들어 107호 병실에 방문객용 의자가 5개가 있습니다. 간호사는 이 병실에 방문자가 5명만 들어갈 수 있도록 허용하고 나머지 방문객들은 밖에서 대기하도록 합니다. 이후 병실의 방문자가 나가면 대기하고 있던 방문자가 병실로 입실합니다. 이렇게 카운터 갯수만큼 공유자원에 접근할 수 있는 방식을 의미합니다.
counter = n; // 가용 갯수 초기화
wait(S) {
while(counter <= 0) {} // 가용 갯수를 모두 사용했다면 대기
counter--; // 가용 갯수 감소
}
signal(S) {
counter++;
}
// CPU를 활용한 lock, unlock 매커니즘
counter = n; // 가용 갯수 초기화
wait(S) {
counter--;
if(counter < 0) {
P.status = "wait"
list.push_back(P);
}
}
signal(S) {
counter++;
if(counter<=0) {
P = list.pop_back();
P.status = "ready";
}
}
// Process Status를 활용한 lock, unlock 매커니즘
Locking 매커니즘을 활용하여 공유 데이터에 접근을 제어합니다. 예를 들어 카페의 공용 화장실은 누군가 화장실에 들어가 있으면 다른 사람들은 대기하고 있어야합니다. 이후 화장실에서 사람이 나와 키를 두고가면 다른 사람이 들어갈 수 있습니다. 즉, 열쇠를 가지고 있을 때만 공유 화장실에 접근할 수 있습니다.
flag = true; // 초기화
wait(S) {
while(!flag) {} // 누군가 Critical Section에 진입했다면 대기한다.
flag = false;
}
signal(S) {
flag = true;
}
// CPU를 활용한 lock, unlock 매커니즘
flag = true; // 초기화
wait(S) {
if(flag) {
flag = false;
}
else {
P.status = "wait";
list.push_back(P);
}
}
signal(S) {
flag = true;
if(!list.empty()) {
P = list.pop_back();
P.status = "ready";
}
}
// Process Status를 활용한 lock, unlock 매커니즘
※ CPU 활용 -> Process State
초기 버전에서 대기하는 프로세스들은 반복 루프를 돌며 대기하여 CPU 리소스를 낭비합니다. 이를 Busy Waiting이라고 부릅니다.
따라서 다음 버전에서 프로세스의 상태를 활용했습니다. 대기하는 프로세스들을 Block 시킨 뒤 Critical Section에 진입이 가능하다면 다시 wakeUp하는 방식을 이용합니다.
프로세스들끼리 서로 자원을 가지고 있고, 다른 프로세스가 소유한 자원을 요구하면서 무한정 대기하는 상황을 말합니다.
이를 해결하기 위해서 OS는 문제가 생긴 사이클의 한 프로세스를 종료하거나, 문제가 생긴 모든 프로세스를 재시작하거나 무시한다. 사실 OS는 정확한 프로그램이기 보다는 프로그램들을 실행하는데 도움을 주는 역할이 크다. 즉, 데그락을 해결하는데 너무 많은 공을 들이면 OS가 가진 원래 목적과 맞지 않는다.
Deadlock 문제 발생 조건
Deadlock은 아래 4가지 조건을 모두 만족해야 발생합니다.
Deadlock 해결 방법