컴퓨터 시스템에서는 데이터를 가지고 연산할때, 데이터가 저장된 곳에서 불러오고 그걸 연산하는 구조를 띄고있음,
예를들면, CPU가 메모리에서 데이터를 읽어 연산, 저장을 한다던가, 프로세스가 각자의 주소공간에서 데이터를 읽어 값을 변경, 저장 한다던가의 형식
여기서 CPU가 여러 프로세스를 멀티 테스킹할때 등의 상황에서 동기화 문제가 발생할 수 있음
공유 데이터의 동시 접근은 데이터의 불일치 문제를 야기할 수 있음
데이터의 일관성을 유지하기 위해서는 협력 프로세스 간의 실행 순서를 정해주는 메커니즘이 필요하게됨
Race condition
Race Condition : 대표적인 동기화문제로, 여러 프로세스가 같은 데이터를 활용하려 할때, 의도치않은 결과가 나오는것
OS에서의 race condition
1번의 경우
커널 모드에서 실행 중에 인터럽트가 발생해서 인터럽트 처리루틴이 수행되는 경우를 그림으로 나타낸것.
양쪽 다 커널 코드이므로, 커널 주소 공간을 공유하게됨, 위 예에서는 count값을 로드하고 난 후 인터럽트가 발생해서 count값을 1빼는 코드가 적용이 안되고
1증가하는 커널의 2번 부분만 적용됨. 해결책으로는 주소 공간의 값을 가져와서 처리하는 코드에 진입할 경우 인터럽트를 disable시키고 종료 되면 enable시켜서
중간에 인터럽트 핸들러가 실행되지 않게 하면됨
A가 커널모드에서 실행 중일떄, 문맥 교환이 일어나서 B가 실행되는 상태를 나타낸 그림
A와 B 모두 count값을 1씩 증가시키는데, B의 경우는 반영이 안돼서 1만 증가됨
해결책으로는 커널 모드에서 수행 중일떄는 다른 프로세스가 CPU를 선점하지 않도록 하면됨, 즉 사용자 모드에서만 문맥 교환이 일어나도록
CPU 여러개가 count값을 변경하는 경우, 어떤 CPU가 마지막으로 count값을 store했는지에 따라 값이 달라짐
해결책으로는 한 CPU가 count값을 접근할떄 락을 걸어서 다른 cpu가 접근을 못하게 막은 다음 count값 store작업이 끝난 후 락을 푸는 방법
Critical Section 문제
해결의 충족 조건
Peterson's 알고리즘
Process i
do{
flag[i] = true; //i가 크리티컬섹션에 들어간다는 flag를 true로 변경
turn = j; // 턴을 프로세스 j에게 넘김
while(flag[j] && turn==j); // 프로세스 j의 차례이고 j가 크리티컬 섹션에 있을때 while문 돌면서 기다림
critical section part
flag[i] = false; // 크리티컬섹션 종료 후 flag를 false로 바꿔서 상대방이 크리티컬섹션에 들어가도록 해줌
remainder section
}while(1);
중간에 아무 지점에서나 CPU를 선점당해도 위의 충족 조건들을 만족하면서, 정상적으로 동작함
하지만 Busy Waiting(=spin lock)문제가 발생함 계속 CPU와 메모리를 쓰면서 기다림(while문 부분)
(반대로, 멀티프로세서에서는 스핀락이 선호되는 경우가 있음, 스핀락중에 락이 풀리면 바로 크리티컬섹션으로 돌입이 가능하기때문에 문맥교환 비용을 아낄 수 있음)
하드웨어적으로 Test & set를 atomic하게 수행할 수 있도록 지원하는 경우 Busy Waiting문제가 해결됨
process i
do {
while(Test_set(lock));
critical section
lock = false;
remainder section
}while(1);
lock이 0이면 test_set이 0을 반환해서 while문 탈출, 1이면 test_set이 1을 반환해서 wait상태
세마포어를 사용해도 결국은 하드웨어의 도움이 없으면 스핀락이 생김 이 스핀락을 해결하기 위한 구현방식
아이디어는 프로세스가 IO작업 중에 블락 상태가 되고, IO작업이 끝나면 ready큐에 들어가는 것 처럼 다른 스레드나 프로세스가 공유 데이터를 사용하고 있으면, 블락상태가 되면서 스핀락이 되지 않고 공유 데이터의 락이 풀리면 그때 Wake up하여 CPU를 점유하는것
typedef struct
{
int value; //세마포어
struct process *L; // wait 큐 프로세스
}semaphore;
P(S):
S.value--; // 자원 흭득
if(S.value < 0) //만약 세마포어의 값이 음수이면 이미 다른 프로세스가 자원을 다가져간 상황 즉 여분의 자원이없음
{
block() //여분의 자원이없으므로 block상태
}
V(S):
S.value++; //자원 반납
if(S.value <= 0) //자원이 0이하라면, 이 자원을 가지고 block된 프로세스나 스레드가 있음을 의미함
{
wakeup(P); //이 자원을 기다리며 block상태인 프로세스를 wakeup해줌
}
Reference
주니온 교수님 유튜브 채널
반효경 교수님 KOCW 운영체제
운영체제 아주 쉬운 세 가지 이야기(OSTEP 번역서)