대학교에서 운영체제를 배울 때 가장 중요하게 배우는 지점이 바로 임계 구역 관련한 부분이다.
영어로는 Critical Section이라고 하며 뜻은 다음과 같다.
먼저 세마포어(semapore)와 뮤텍스(mutex)의 개념을 알고 가자.
뮤텍스(mutex)
스레드들 간에서 공유가 배제되는 객체. 파일과 같은 공유 자원이 수행 중 오직 한 프로그램이나 스레드에게만 소유되어야 할 필요가 있을 때 그 자원에 대한 뮤텍스 객체를 생성시킨다. 뮤텍스가 비신호 상태이면 프로그램은 자원을 점유하여 사용한 후 이를 반환하고, 다른 프로그램 또는 다른 스레드가 자원을 사용 중 즉, 뮤텍스가 신호 상태이면 대기 상태로 들어가 끝나기를 기다린다. 뮤텍스는 여러 면에서 크리티컬 섹션과 비슷하고, 대신 사용할 수도 있지만 이름을 가질 수 있다는 점에서 크리티컬 섹션보다 우월하다.
뮤텍스(상호배제)는 교착상태의 4가지 필요조건 중 하나이다.
참고로 교착상태의 4가지 필요조건은 다음과 같다.
상호배제(Mutual exclusion) : 프로세스들이 필요로 하는 자원에 대해 배타적인 통제권을 요구한다.
점유대기(Hold and wait) : 프로세스가 할당된 자원을 가진 상태에서 다른 자원을 기다린다.
비선점(No preemption) : 프로세스가 어떤 자원의 사용을 끝낼 때까지 그 자원을 뺏을 수 없다.
순환대기(Circular wait) : 각 프로세스는 순환적으로 다음 프로세스가 요구하는 자원을 가지고 있다.
세마포어(semaphore)
철도의 까치발 신호기 또는 해군의 수기 신호라는 뜻으로, 운영 체계 또는 프로그램 작성 내에서 공유 자원에 대한 접속을 제어하기 위해 사용되는 신호. 병행 내지 병렬로 동작되는 둘 이상의 프로세서 사이에서 마이크로프로세서 시간이나 입출력 접속구와 같은 공유 자원을 동시에 사용할 수 없기 때문에, 한 프로세서가 사용하고 있는 동안에 세마포어를 세워서 다른 프로세서를 대기시키고 사용이 끝나면 해제시키는 방법으로 사용한다.
뮤텍스와 세마포어는 비슷하지만 이렇게 이해하면 된다.
만약 우리는 방에(자원) 들어가고 싶을 때
뮤텍스는 방에 들어가기 위한 열쇠의 갯수이며 세마포어는 빈 방 열쇠의 갯수이다.
즉, 뮤텍스는 방에 들어갈 수 있는 열쇠를 한명이 갖고 있다면 그 사람이 나와야지만 다른 사람이 그 열쇠를 얻어서 방에 다시 들어갈 수 있다.
여기서 기다리는 사람은 스레드가 될 것이다.
그리고 세마포어는 방이 4개이면 열쇠도 4개이고 한 사람이 들어갈 때마다 방과 열쇠의 갯수가 1개씩 감소하고 둘다 0이 되면 다른사람들은 못들어가는 개념이다. 먼저 점유하고 있던 사람이 일을 마치고 나오면 방과 열쇠가 1개씩 늘어나므로 그때서야 들어갈 수가 있다.
뮤택스는 값이 1인 세마포어와 같은 개념이라고 볼 수 있다.
Critical Section
임계 구역으로 프로세스 또는 스레드들이 공통 변수들에 접근, 및 수정하고 테이블을 갱신하며, 파일의 읽기, 쓰기 작업들을 수행하는 영역을 말한다. 즉 race condition이 발생하는 영역을 뜻한다. 이미 한 스레드가 Critical Section에 들어갔을 때 다른 스레드들은 이 곳으로의 접근을 막아야한다.
한 프로세스 혹은 한 스레드가 메모리의 어떤 부분을 접근하고 있을 때 다른 프로세스 혹은 스레드가 그 부분을 접근하여 값을 변경하면 안된다. 따라서 그 부분을 막기 위해 우리는 동기화를 해야한다는 개념이 나오는 것이다.
비동기 count 코드와 동기 count 코드는 아래에 참조로 넣어두겠다.
예를 들어 c언어로 count 프로그램을 작성해보자.
이 프로그램을 실행해보면 다음과 같은 결과가 나왔다.
벌써 3번째 줄에서 3이 아니라 4가 나왔다. add가 되어서 덧셈은 되었지만 순서가 뒤바뀌었다는 이야기이다.
그래서 동기화가 필요하다. 동기화가 되면 다음처럼 나온다.
프로그램을 설명을 하면 덧셈 함수 3개(add1, add2, add3)와 뺄셈 함수 3개(sub1, sub2, sub3)에 따라서 count가 +-1씩 증감이 일어난다.
먼저 비동기화 코드를 설명을 하면 한 프로그램(프로세스)에 변수 count 1개에 접근하는 스레드를 6개 만든다. (덧셈3개, 뺄셈3개)
그래서 비동기로 돌리면 각각 돌아서 덧셈과 뺄셈이 무작위로 발생하며 count가 증감이 일어난다.
따라서 이 때에는 우리가 결과를 예측할 수 없다.
하지만 우리는 예측가능하도록 동기화를 진행하기 위해 뮤택스(mutex)를 사용하고 상호 배제를 보장하게 해야한다.
한 스레드가 자원을 사용하기 위해 락(lock)을 걸면 다른 스레드가 자원을 이용하지 못하고 끝나면 lock을 풀어줘야 다른 스레드가 자원에 접근할 수 있다.
자원에 접근 할 때 누가 사용하지 못하도록 lock을 걸고 풀고를 반복하는 개념이다.
비동기 count 코드
#include <stdio.h>
#include <pthread.h>
int count=0;
int counter=0;
void *add1(void *data){
int tmp;
while(1){
while(counter == 10)
;
count++;
sleep(1);
printf("add1 count= %d\n",count);
tmp=counter;
sleep(1);
tmp=tmp+1;
counter=tmp;
}
}
void *add2(void *data){
int tmp;
while(1){
while(counter == 10)
;
count++;
printf("add2 count= %d\n",count);
tmp=counter;
sleep(1);
tmp=tmp+1;
counter=tmp;
}
}
void *add3(void *data){
int tmp;
while(1){
while(counter == 10)
;
count++;
printf("add3 count= %d\n",count);
tmp=counter;
sleep(1);
tmp=tmp+1;
counter=tmp;
}
}
void *sub1(void *data){
int tmp;
while(1){
while(counter== 0)
;
count--;
printf("sub1 count= %d \n",count);
tmp=counter;
sleep(1);
tmp=tmp-1;
counter=tmp;
}
}
void *sub2(void *data){int tmp;
while(1){
while(counter== 0)
;
count--;
printf("sub1 count= %d \n",count);
tmp=counter;
sleep(1);
tmp=tmp-1;
counter=tmp;
}
}
void *sub3(void *data){
int tmp;
while(1){
while(counter== 0)
;
count--;
printf("sub1 count= %d \n",count);
tmp=counter;
sleep(1);
tmp=tmp-1;
counter=tmp;
}}
int main(){
int i=0;
pthread_t pthread[5];
pthread_create(&pthread[0],NULL,add1,NULL);
pthread_create(&pthread[1],NULL,sub1,NULL);
pthread_create(&pthread[2],NULL,add2,NULL);
pthread_create(&pthread[3],NULL,sub2,NULL);
pthread_create(&pthread[4],NULL,add3,NULL);
pthread_create(&pthread[5],NULL,sub3,NULL);
for(i=0;i<6;i++)
pthread_join(pthread[i],NULL);
return 0;
}
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
int count=0;
int counter=0;
pthread_mutex_t mutex;
sem_t full,empty;
void *add1(void *data)
{
int tmp;
while(1)
{
sem_wait(&full);
pthread_mutex_lock(&mutex);
count++;
printf("add1 count= %d\n",count);
sleep(1);
pthread_mutex_unlock(&mutex);
sem_post(&empty);
}
}
void *add2(void *data)
{
int tmp;
while(1)
{
sem_wait(&full);
pthread_mutex_lock(&mutex);
count++;
printf("add2 count= %d\n",count);
sleep(1);
pthread_mutex_unlock(&mutex);
sem_post(&empty);
}
}
void *add3(void *data)
{
int tmp;
while(1)
{
sem_wait(&full);
pthread_mutex_lock(&mutex);
count++;
printf("add3 count= %d\n",count);
sleep(1);
pthread_mutex_unlock(&mutex);
sem_post(&empty);
}
}
void *sub1(void *data)
{
int tmp;
while(1)
{
sem_wait(&empty);
pthread_mutex_lock(&mutex);
count--;
printf("sub1 count= %d \n",count);
sleep(1);
pthread_mutex_unlock(&mutex);
sem_post(&full);
}
}void *sub2(void *data)
{
int tmp;
while(1)
{
sem_wait(&empty);
pthread_mutex_lock(&mutex);
count--;
printf("sub2 count= %d \n",count);
sleep(1);
pthread_mutex_unlock(&mutex);
sem_post(&full);
}
}
void *sub3(void *data)
{
int tmp;
while(1)
{
sem_wait(&empty);
pthread_mutex_lock(&mutex);
count--;
printf("sub3 count= %d \n",count);
sleep(1);
pthread_mutex_unlock(&mutex);
sem_post(&full);
}
}
int main(){
int i=0;
pthread_t pthread[5];
sem_init(&empty,0,0);
sem_init(&full,0,10);
pthread_mutex_init(&mutex,NULL);
pthread_create(&pthread[0],NULL,add1,NULL);
pthread_create(&pthread[1],NULL,sub1,NULL);
pthread_create(&pthread[2],NULL,add2,NULL);
pthread_create(&pthread[3],NULL,sub2,NULL);
pthread_create(&pthread[4],NULL,add3,NULL);
pthread_create(&pthread[5],NULL,sub3,NULL);
for(i=0;i<6;i++)
pthread_join(pthread[i],NULL);
return 0;
}
이상으로 상호 배제 동기/비동기, 뮤텍스, 세마포어에 관한 설명을 마치겠다.
'Programming > etc' 카테고리의 다른 글
HTTP 통신, 쿠키, 세션 이란? (0) | 2018.06.11 |
---|---|
실제 내 PC의 IP 확인하는 방법 (0) | 2018.05.29 |
[정보보안기사 대비] 용어 정리 9 (0) | 2018.04.29 |
RAID 구조 종류(RAID 0부터 10까지)와 구성 방식 자세한 설명 (0) | 2018.04.28 |
[정보보안기사 대비] 용어 정리 8 (0) | 2018.04.28 |