[운영체제] Mutex (Mutual exclusion)

Jaehwan Lee·2021년 10월 25일
0

운영체제

목록 보기
3/3
post-thumbnail

1) Mutex란 무엇인가?

여러 개의 프로세스가 동시에 공유된 자원에 동시에 접근하게 되면 문제가 발생할 수 있다.

예를 들어, 두 개의 프로세스가 공유하는 1개의 변수를 업데이트 하려고 하는데, 하나의 쓰레드는 변수를 0으로 다른 하나는 1로 변경을 시도한다고 하자. 만약 이러한 업데이트가 동시에 일어난다면 하나는 0으로 변경이 되었지만 다른 하나는 1로 변경되는 문제가 발생할 수도 있다. 즉, 데이터 불일치 문제가 야기될 수 있다.

이를 해결하기 위해 공유하는 데이터를 한 번에 하나의 프로세스만 접근할 수 있도록 제한을 두어야 한다. 즉, 두 프로세스를 동기화해야 한다.

뮤텍스(Mutex)란 ‘Mutual Exclusion'의 줄임 말로써 프로세스가 서로 간에 공유 자원의 동시 접근을 허용하지 않기 위한 기술이다. 간단히 말하자면, 자원에 접근하기 위해 각 프로세스는 락을 획득하여 접근 허용 권한을 얻어야 한다. 한 프로세스가 접근 권한을 허용 받으면 다른 프로세스는 락을 반환하기 전까지는 자원 사용이 불가하다. 결국 두 프로세스가 동시에 접근하는 일은 없게 된다.


2) Pthread를 이용한 Mutex 구현

pthread 라이브러리를 활용하여 공유 자원 동시 접근을 막을 수 있는 Mutex를 구현할 수 있다.

Mutex 구현을 위해 필요한 4가지 함수는 아래와 같으며 성공 시 모두 0을 반환한다.

  • Mutex Initialize
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

mutex는 사용하기 전에 초기화되어야 하며, 인자로는 mutex 포인터와 특성을 전달한다.

  • Mutex Lock
int pthread_mutex_lock(pthread_mutex_t *mutex);

Mutex를 lock하여 다른 쓰레드가 자원을 접근할 수 없도록 한다. 만약에 이미 mutex를 잠근 프로세스가 존재하는 상태에서 이 함수를 다른 프로세스가 또 호출하게 되면, 이전에 진입한 프로세스가 빠져 나올 때까지 대기 상태에 있어야 한다.

  • Mutex Unlock
int pthread_mutex_unlock(pthread_mutex_t *mutex);

Mutex의 lock을 풀 때 사용한다. Mutex를 lock한 프로세스는 이후 다시 unlock해야 한다. 그렇지 않으면 다른 프로세스는 진입할 수가 없다. 이러한 상황이 발생하면 교착 상태(Deadlock)가 된다.

  • Mutex Destroy
int pthread_mutex_destroy(pthread_mutex_t *mutex);

Mutex와 이에 관련된 자원의 lock을 해제할 때 사용되는 함수이다.


3) Mutex 예제 코드

예제 코드의 시나리오는 다음과 같다.

1) thread를 2개 생성하고 각각은 ncount 라는 하나의 변수에 접근하여 1씩 증가시키는 반복문(10번)을 실행한다.

2) 각각의 thread는 반복문을 실행하기 전 pthread_mutex_lock을 실행한다.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h> // for thread

int ncount; // shared data
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // thread Initializing

void* thread1(void *data) {
    pthread_mutex_lock(&mutex); // Locking   
    
    for (int i = 0; i < 10; i++) {
        printf("thread1: %d\n", ncount);
        ncount++;
        sleep(1);
    }

    pthread_mutex_unlock(&mutex); // Unlocking
} 

void* thread2(void *data) {    
    // Wait until 'thread1' unlock
    pthread_mutex_lock(&mutex); // Locking
    
    for (int i = 0; i < 10; i++) {
        printf("thread2: %d\n", ncount);
        ncount++;
        sleep(1);
    }

    pthread_mutex_unlock(&mutex); // Unlocking
} 

int main() {  
    int thr_id;
    pthread_t p_thread[2]; // 2 thread
    int status;
    int a = 1;
    ncount = 0;
		
    thr_id = pthread_create(&p_thread[0], NULL, thread1, (void*)&a);
    sleep(1);
    thr_id = pthread_create(&p_thread[1], NULL, thread2, (void*)&a);

    pthread_join(p_thread[0], (void*) &status);
    pthread_join(p_thread[1], (void*) &status);

    status = pthread_mutex_destroy(&mutex); // return 0 if successful

    printf("\ncode = %d", status);
    printf("\nProgram is end!\n");
   
    return 0;
}

4) Mutex 예제 코드 실행 결과

Mutex의 효과를 확인하기 위해 mutex lock을 사용했을 때와 사용하지 않았을 때의 결과를 출력해 보았다.

1) Mutex lock을 하지 않았을 때

두 thread가 번갈아가며 한 변수에 접근 하는 것을 볼 수 있다.

그림 1) Mutex Lock을 하지 않고 컴파일 했을 때의 실행 결과 화면

2) Mutex lock을 하였을 때

Mutex lock을 하게 되면 한 변수에 동시에 접근하지 않고 순차적으로 접근하는 것을 알 수 있다.

그림 2) Mutex Lock을 하고 컴파일 했을 때의 실행 결과 화면


참고자료

profile
느리더라도 꾸준히 멈춤 없이

0개의 댓글