새소식

인기 검색어

컴퓨터공학/운영체제

6장 스레드 동기화

  • -

Q1 : 멀티스레드 응용프로그램을 작성하면 반드시 스레드 동기화가 필요한지에 대해 자신의 생각을 간단히 논하라.

 

A1 : 반드시 필요하진 않은것 같다 멀티스레드 응용프로그램이여도 공유데이터에 접근하지않고 개별데이터로 실행되는 프로그램이라면 스레드 동기화가 필요하지 않기 때문이다.

 

 

Q2 : 스레드 동기화가 무엇인지 나름대로 설명하는 긴 문장을, 상호 배재, 임계 구역, 멀티스레드, 원자 명령의 단어를 연결하여 작성해보라.

 

A2 : 멀티스레드 응용프로그램을 실행중 다수의 스레드가 공유데이터에 동시에 접근하여 공유 데이터가 훼손되어 이를 막기위한 방법이 필요해졌다. 스레드 동기화는 스레드가 공유 데이터에 접근하는 코드의 영역인 임계구역에 한 스레드만 접근하게 하는 기술이다.

임계구역을 어느 시점에서 한 스레드만 접근을 허용하고 다른 스레드가 현재 사용중인 임계구역에 접근하는 것을 금지하는 행위를 상호배제라고 하는데 임계구역의 진입/진출 시점에 코드를 구현하여 하드웨어적인 방법을 사용한다.

상호배제 방법으로 인터럽트 금지 서비스를 사용한다. 인터럽트 금지 서비스는 스레드가 임계구역으로 접근하면 인터럽트가 발생해도 무시하고 임계구역이 끝나면 인터럽트를 허용하는 방식이다. 하지만 이 방법은 모든 인터럽트를 무시하고 멀티코어 CPU에선 임계구역에 들어온 CPU의 인터럽트는 무시해도 다른 CPU의 인터럽트는 무시하지 못하는 문제가 있다. 그래서 임계구역 진입/진출 시점에서 lock 변수를 사용하여 임계구역의 진출여부를 결정하는 방법을 사용하게 되었다. 하지만 lock 변수를 읽고 lock 변수를 변경하는 사이에 인터럽트가 발생하면 임계구역에 여러개의 CPU가 들어오는 문제점이 있었고 이를 방지하기 위해 lock 변수를 읽고 변경하는 과정을 하나로 합친 원자 명령이 등장하여 사용되고 있다.

 

 

 

Q3 : 인터럽트를 금지하면 상호배제가 이루어진다. 상호배제가 이루어지는 과정을 그림으로 그리고 간단히 설명해보라. 그렇지만, 인터럽트를 금지하는 방법은 완벽한 해결책에 되지 못한다. 그 이유는 무엇인가?

 

A3 :  인터럽트 금지 서비스는 위의 A2의 답변과 마찬가지로 모든 인터럽트를 무시하는 문제와 한 CPU의 인터럽트 금지로 다른 CPU에게까지 인터럽트 금지가 불가하므로 멀티코어 CPU나 다중 CPU를 가진 시스템에서 활용이 불가 하기 때문입니다.

 

 

*(4~6 번 문제를 위해) 다음은 lock 변수를 이용하여 상호 배제를 시도하는 그림이다.

Q4 : 이 코드를 사용하면 임계 구역으로 진입할 때 상호 배제가 잘 이루어지지 않는다. 구체적 인 이유를 그림과 함께 설명하라.

 

A4 : 아래와 같이 lock 변수 값을 꺼내오고 lock 변수값을 바꾸기 전에 인터럽트가 발생하여 T1이 중단되고 있는 사이에 T2가 임계영역에 진입하기 위해 lock 변수를 확인하면 lock변수가 변경되지 않았기에 0을 읽어오게 되고 lcok변수를 1로 변경한 뒤 임계구역에 진입하게 된다. T1의 인터럽트가 끝난뒤 TCB1에서 ax를 읽으면 0이기 때문에 T1도 임계구역에 진입하게 되어 임계구역에 T1, T2가 들어가 충돌이 일어나게 된다.

 

 

Q5 : 근본적인 문제점을 한 줄로 설명하고, 원자 명령을 이용하여 위의 코드를 수정하고 상호 배제가 잘 이루어지는 것을 그림과 함께 설명하라.

 

A5 : 임계영역에 진입여부를 확인하기 위해 lock변수를 읽어오고 변경하는 과정사이에 인터럽트가 발생했기 때문이다. 두개의 명령을 하나의 명령으로 처리하면 해결된다.

 

 

Q6 : CPU마다 서로 다른 원자명령을 두고 있는가, 모두 같은 것인가?

 

A6 : CPU마다 명령이 서로 다르지만 통일하여 원자명령이라 부른다.

 

 

Q7 : 뮤텍스와 스핀락은 각각 어떤 상황에서 적합한지 간단히 비교 설명하라.

 

A7 : 락이 잠기는 시간이 긴 응용프로그램, 단일 CPU를 가진 시스템 사용자 응용프로그램은 뮤텍스가,

락이 잠기는 시간이 짧은 프로그램, 다중 CPU를 가진 시스템, 커널코드는 스핀락이 유리하다.

 

 

Q8 : n명만 동시에 사용할 수 있는 데이터베이스가 있다. 데이터베이스에 접근을 다루기 위해 뮤텍스, 스핀락, 세마포 중 어떤 것이 적합한가? 적합하지 않는 것을 왜 적합하지 않는가?

 

A8 : 세마포가 적합하다. 뮤텍스나 스핀락은 락이라는 변수 하나를 가지고 여러 스레드들 중 하나의 스레드만 임계구역에 진입여부를 결정하는 방법이기 때문이다. n명이 동시에 사용할 수 있는 데이터베이스는 비어 있는 아무자리나 사용하면 되기 때문에 세마포가 적합하다.

 

 

Q9 : 우선순위 역전은 어떤 조건과 상황에서 발생하는지 숙지하고 우선순위 역전이 일어나는 과정을 사례를 들어 보여라.

 

A9 : T1,T2,T3가 각 순서대로 우선순위가 높고 T1, T3는 공유변수를 사용하고 T2는 공유변수를 사용하지 않는다고 가정했을 때

T3가 실행중 T1이 도착하면 우선순위가 높더라도 P연산후 T3가 공유변수를 사용중인 것을 바꿀수 없기에 T1은 P연산내 잠들게 된다.

그 뒤 T3가 다시 연산하는 도중 T2가 도착하면 T2는 공유변수를 사용하지 않기 때문에 T3보다 우선순위가 높은 T2를 실행하게 되면 T2보다 우선순위가 높은 T1은 연산되지 않는 우선순위 역전이 발생한다.

 

 

Q10 : sum 변수에 1에서 40000까지 더하기 위해 4개의 스레드가 동시에 실행시킨 ‘5주차 11번 문제’는 스레드가 동기화가 되어 있지 않아 sum 변수에 정확한 합을 만들어내지 못한다. pthread의 뮤텍스를 이용하는 코드와 스핀락을 이용하는 코드를 각각 완성하여, 코드와 실행 결과를 캡쳐하여 제출하라.

 

A10 :

 

Mutex.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void* runner(void *param);
int sum = 0;
char num[4][20];
pthread_mutex_t lock;

int main(){
    pthread_t tid[4];
    pthread_attr_t attr[4];
    
    int i;
    
    pthread_mutex_init(&lock,NULL);
    
    for(i=0; i<4; i++){
        sprintf(&num[i][0],"%d",1+10000*i);
        pthread_attr_init(&attr[i]);
        pthread_create(&tid[i],&attr[i],runner,&num[i][0]);
    }

    for(i=0; i<4; i++) {
        pthread_join(tid[i],NULL);
    }
        
    printf("sum = %d\n",sum);

    pthread_mutex_destroy(&lock);
 
    return 0;
}

void* runner(void *param){
    int to = atoi(param);
    int i;

    pthread_mutex_lock(&lock);
    
    for(i=to; i<=to+9999; i++) {
        sum += i;
    }

    pthread_mutex_unlock(&lock);
}

 

Spinlock.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void* runner(void *param);
int sum = 0;
char num[4][20];
pthread_spinlock_t lock;

int main(){
    pthread_t tid[4];
    pthread_attr_t attr[4];
    
    int i;
    
    pthread_spin_init(&lock,PTHREAD_PROCESS_PRIVATE);
    
    for(i=0; i<4; i++){
        sprintf(&num[i][0],"%d",1+10000*i);
        pthread_attr_init(&attr[i]);
        pthread_create(&tid[i],&attr[i],runner,&num[i][0]);
    }
    for(i=0; i<4; i++) {
        pthread_join(tid[i],NULL);
    }

    printf("sum = %d\n",sum);
    
    pthread_spin_destroy(&lock);
    
    return 0;
}

void* runner(void *param){
    int to = atoi(param);
    int i;
    
    pthread_spin_lock(&lock);
    
    for(i=to; i<=to+9999; i++) {
        sum += i;
    }
    pthread_spin_unlock(&lock)
}

 

'컴퓨터공학 > 운영체제' 카테고리의 다른 글

9장 페이징 메모리  (0) 2023.05.28
7장 교착상태  (0) 2023.05.28
5장 CPU 스케줄링  (0) 2022.01.13
4장 스레드와 멀티태스킹  (0) 2022.01.10
3장 프로세스와 프로세스 관리  (0) 2022.01.03
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.