본문 바로가기

운영체제

명품 운영체제 연습문제 4장

생능출판사 명품 운영체제

 

본 연습문제들은 작성자 본인이 푼것이라 틀릴 수 도 있습니다.

 

[개념체크]

 

1. 스레드가 도입된 배경으로 프로세스의 문제점을 잘못 설명한 것은?

④ 프로세스 스케줄링의 복잡성을 줄이려 했다

 

2. 프로세스와 스레드의 관계에 대한 설명으로 틀린 것은?

③ 프로세스는 자신에게 속한 모든 스레드의 부모이다

 

3. 멀티태스킹 프로그램을 작성하는데 있어서 각 태스크를 프로세스로 만드는 것과 스레드로 만드는 방법 중 스레드로 만드는 방법이 유리한 이유로 맞는 것은?

② 프로세스들은 주소 공간이 완전히 분리되어 있어 공유 공간을 만들기 위해 운영체제의 도움을 받아야 하지만, 멀티스레드를 이용하는 경우 프로세스 내에 공유 변수 등을 통해 쉽게 통신할 수 있기 때문이다

 

4. 스레드에 관한 정보를 저장하는 구조체를 무성이라고 부르는가?

TCB

 

5. 다음 중 다른 한가지는?

TLS(Thread Local Storage)

 

6. 다음 중 프로세스와 스레드 중 선택하여 문장을 완성하라.

운영체제의 실행 단위는 ( 스레드 ) 이며, ( 프로세스 ) 는 ( 스레드 ) 들이 공유하는 환경을 제공한다. ( 프로세스 ) 들은 각각 독립적인 메모리  공간에서 실행되므로 ( 프로세스 ) 사이에 데이터를 주고받는데 심각한 어려움이 있다. 응용프로그램을 실행시키기 위해 운영체제는 ( 프로세스 ) 를 만든다. 하나의 ( 프로세스 ) 가 실행되면 반드시 한 개의 ( 스레드 ) 가 자동으로 만들어지고 이것은 ( 스레드 ) 라고 부른다. 하나의 ( 프로세스 ) 는 여러 개의 ( 스레드 ) 를 가질 수 있다. ( 프로세스 ) 가 실행중이라는 뜻은 ( 프로세스 ) 속한 ( 스레드 ) 중 한 개의 ( 스레드 ) 가 현재 CPU에 의해 실행되고 있음을 뜻한다. 그러므로 운영체제 스케줄러에 의해 스케줄되는 단위는 ( 스레드 ) 이며, ( 프로세스 ) 에 속한 모든 ( 스레드 ) 가 종료할 때 ( 프로세스 ) 도 종료된다.

 

7.  다음 설명은 concurrency와 paralleism 중 어떤 것인지 선택하라.

(1) 1개의 CPU가 여러 스레드를 번갈아 실행할 때 cocurrency

(2) 2개의 CPU에서 2개의 스레드가 각각 동시에 실행될 때 paralleism

(3) CPU가 스레드의 입출력으로 인해 유휴(idle) 상태에 있지 않고 다른 스레드를 실행할 때 cocurrency

(4) 멀티 코어 CPU가 여러 스레드를 같은 시간에 동시에 실행할 때 paralleism

 

8. 다음 그림은 스레드 T1, T2, T3 가 실행되는 과정을 보여준다. cocurrency인가, parallelism인가?

cocurrency 이다

 

9. 스레드가 활동하는 코드, 데이터, 힙, 스택은 어떤 메모리 공간에 형성되는가?

스레드가 속한 프로세스 주소 공간 내에

 

10. 스레드의 주소 공간에 대한 설명으로 틀린 것은?

스레드 코드는 프로세스의 공간 밖에 별도의 공간에 적재된다

 

11. 스레드 로컬 스토리지(TLS)란 어떤 메모리 공간인가?

① 다른 스레드가 접근할 수 없는 스레드만의 사적인 공간이다

 

12. 스레드 A가 malloc(100)을 이용하여 동적 할당받은 100바이트 공간에 대해 틀리게 설명한 것은?

스레드 A가 종료할 때 할당받은 100바이트 공간은 자동 반환되지 않는다

 

13. TCB에 저장되는 내용이 아닌 것은?

스레드 크기

 

14. TCB에 저장되는 내용이 아닌 것은?

스레드 시작 시간

 

15. 다음 중 스레드 스케줄링이 일어나는 시점이 아닌 것은?

스레드가 시스템 호출을 하여 커널로 진입한 직후

 

16. 프로세스 컨텍스트 스위칭과 스레드 컨텍스트 스위칭을 비교하여 잘 설명하지 못한 것은?

프로세스 컨텍스트 스위칭이 개념적으로 스레드 컨텍스트 스위칭보다 단순하다

 

17. 스레드 운용에 관한 설명 중 틀린 것은?

스레드가 종료되면 스레드가 속한 프로세스도 종료된다

 

18. 스레드 라이브러리에 포함된 것이 아닌 것은?

스레드에서 파일을 여는 함수

 

19. 커널 레벨 스레드의 정의는 무엇인가?

커널에 의해 스케줄되는 스레드

 

20. 사용자 레벨 스레드의 정의는 무엇인가?

스레드 라이브러리에 의해 스케줄되는 스레드

 

21. 사용자 레벨 스레드의 장점이 아닌 것은?

여러 스레드가 각 코어에서 동시에 실행될 수 있기 때문에 멀티 코어 CPU를 가진 시스템에 적합하다

 

22. 커널 레벨 스레드의 장점은?

여러 스레드가 각 코어에서 동시에 실행될 수 있기 때문에 멀티 코어 CPU를 가진 시스템에 적합하다

 

23. 최근 들어 운영체제는 사용자가 만든 스레드를 커널 레벨 스레드로 구현하는 추세이다. 그 이유는 무엇인가?

① 응용프로그램에서 생성한 각 스레드를 멀티 코어 CPU에 할당하여 응용프로그램 실행에 높은 병렬성을 얻을 수 있기 때문

 

24. N개의 사용자 레벨 스레드를 1개의 커널 레벨 스레드로 매핑하는 N:1 매핑의 최대 단점은 응용프로그램에 속한 한개의 사용자 레벨 스레드가 입출력을 수행하여 볼륵 상태가 되면 응용프로그램 내의 다른 모든 사용자 레벨 스레드가 스케줄(실행)될 수 없다는 점이다. 이 과정을 자세히 설명하라.

사용자 레벨 스레드가 입출력을 수행하여 커널 레벨 스레드가 Blocked 상태가 되면 N:1 매핑이기 때문에 다른 사용자 레벨 스레드가 있음에도 불구하고 응용프로그램 전체가 중단된다. Blocked 상태가 되면 CPU 코어가 다른 커널 레벨 스레드를 할당해 다른 작업을하다 Blocked 상태가 Ready 상태로 바뀌어 커널 스케줄러에 의해 스케줄될 때 까지 기다려야한다.

 

25. 사용자 레벨 스레드와 커널 레벨 스레드의 매핑 기법으로 최근에 가장 많이 사용하는 것은?

② 1:1

 

26. 사용자 레벨 스레드와 커널 레벨 스레드의 매핑 기법으로 1:1 기법을 현재 가장 많이 사용하는 이유는?

매핑 개념이 단순하여 구현하기쉽다. 높은 병렬성을 제공하기 때문에 멀티프로세서를 가진 현대의 컴퓨터 시스템에 매우 적합하다. N:1 모델과 달리, 사용자 레벨 스레드 중 하나가 시스템 호출 중 블록 상태가 되어도, 응용프로그램 내 다른사용자 레벨 스레드는 여전히 스케줄링 가능하므로 응용프로그램 전체가 중단되는 일은없다. N:M 모델은 매핑과 스케줄링 과정이 복잡하여 현대의 운영체제에서는 거의 사용하지 않는다.

 

 

[복합 문제]

 

1. 그림 4-5의 맛보기 프로그램을 참고하여, 리눅스에서 다음 5가지 조건에 부합하느 멀티스레드 C 응용프로그램을 작성하라. 4개의 스레드를 활용하여 1에서 40000까지의 합을 구하여 출력한다.

(1) pthread 라이브러리를 이용하여 작성하라.

(2) 응용프로그램에 0으로 초기화된 전역 변수 int sum[4]를 선언하라.

(3) 스레드로 실행할 함수의 이름을 runner로 하라.

(4) main()에서 pthread_create() 함수를 활용하여 4개의 스레드를 연속적으로 생성하여 4개의 스레드를 동시에 실행시켜라.

- 스레드 1 : 1~10000 까지의 합을 구하고 sum[0]에 저장
pthread_create(... ... runner,"1"); //1에서 10000까지 합 구하기
- 스레드 2 : 10001~20000 까지의 합을 구하고 sum[1]에 저장
pthread_create(... ... runner,"10001"); //10001에서 20000까지 합 구하기
- 스레드 3 : 20001~30000 까지의 합을 구하고 sum[2]에 저장
pthread_create(... ... runner,"20001"); //20001에서 30000까지 합 구하기
- 스레드 4 : 30001~40000 까지의 합을 구하고 sum[3]에 저장
pthread_create(... ... runner,"30001"); //30001에서 40000까지 합 구하기

(5) main()은 4개의 스레드가 모두 종료하기를 기다린 후 sum[] 배열의 값을 모두 합쳐 그 결과를 화면에 출력하라. 다음은 이 프로그램의 이름이 prac4_1.c라고 할 때 컴파일 과정과 실행 결과를 보여준다.

$ gcc -o prac4_1 prac4_1.c -lpthread
$ ./prac4_1
1에서 40000까지 4개의 스레드가 계산한 총 합은 800020000
$

 

 

#include <pthread.h> // pthread 라이브러리를 사용하기 위해 필요한 헤더 파일 
#include <stdio.h>
#include <stdlib.h>

void* runner(void *param); // 스레드로 작동할 코드(함수) 
int total = 0; // main 스레드와 runner가 공유하는 전역 변수  
int sum[4] = { 0 };
int j = 0;
int main() {
        pthread_t tid1; //  스레드의 id를 저장할 정수형 변수 
        pthread_t tid2; //  스레드의 id를 저장할 정수형 변수 
        pthread_t tid3; //  스레드의 id를 저장할 정수형 변수 
        pthread_t tid4; //  스레드의 id를 저장할 정수형 변수 
        pthread_attr_t attr; // 스레드 정보를 담을 구조체 

        pthread_attr_init(&attr); // 디폴트 값으로 attr 초기화 
        pthread_create(&tid1, &attr, runner, "1"); // runner 스레드 생성
        pthread_create(&tid2, &attr, runner, "10001"); // runner 스레드 생성 
        pthread_create(&tid3, &attr, runner, "20001"); // runner 스레드 생성 
        pthread_create(&tid4, &attr, runner, "30001"); // runner 스레드 생성 
	// 스레드가 생성된 수 커널에 의해 언젠가 스케줄되어 실행 
        pthread_join(tid1, NULL); // tid 번호의 스레드 종료를 기다림
        pthread_join(tid2, NULL); // tid 번호의 스레드 종료를 기다림 
        pthread_join(tid3, NULL); // tid 번호의 스레드 종료를 기다림 
        pthread_join(tid4, NULL); // tid 번호의 스레드 종료를 기다림 
        for (int i = 0; i < 4; i++) {
            total += sum[i];
        }
	printf("1에서 40000까지 4개의 스레드가 계산한 총 합은 %d\n", total);
}

void* runner(void *param) { // param에 "값" 전달 
        int to =  atoi(param); // to = "값"
        int tmp = 0;
        total = 0;
        for (int i = to; i <= to + 9999; i++) { // to에서 to+9999까지 합계산 
            tmp += i;
        }
        sum[j] += tmp;
        j++;
}

 

2. 앞의 문제 1을 수정하여 리눅스에서 5가지 조건에 부합하는 멀티스레드 C 응용프로그램을 작성하라. 4개의 스레드를 활용하여 1에서 40000까지의 합을 구하여 출력한다.

(1) pthread 라이브러리를 이용하여 작성하라

(2) 응용프로그램에 전역 변수 int sum을 선언하고 0을 초기화하라.

(3) 스레드로 실행할 함수의 이름은 runner로 하라

(4) main에서 pthread_create() 함수를 활용하여 4개의 스레드를 연속적으로 생성하여 4개의 스레드를 동시에 실행시켜라.

- 스레드 1 : 1~10000 까지의 합을 구하고 sum에 합산
pthread_create(... ... runner,"1"); //1에서 10000까지 합을 sum에 합산
- 스레드 2 : 10001~20000 까지의 합을 구하고 sum에 합산
pthread_create(... ... runner,"10001"); //10001에서 20000까지 합을 sum에 합산
- 스레드 3 : 20001~30000 까지의 합을 구하고 sum에 합산
pthread_create(... ... runner,"20001"); //20001에서 30000까지 합을 sum에 합산
- 스레드 4 : 30001~40000 까지의 합을 구하고 sum에 합산
pthread_create(... ... runner,"30001"); //30001에서 40000까지 합을 sum에 합산

(5) main은 4개의 스레드가 모두 종료하기를 기다린 후 sum 값을 화면에 출력하라. 다음은 이 프로그램의 이름이 prac4_2.c라고 할 때 컴파일 과정과 여러 번의 실행결과를 보여준다. ( 실행결과가 실행할 때 마다 달라진다. )

$ gcc -o prac4_2 prac4_2.c -lpthread
$ ./prac4_2
1에서 40000까지의 스레드가 합친 sum 변수의 값은 583862451
$ ./prac4_2
1에서 40000까지의 스레드가 합친 sum 변수의 값은 640209990
$ ./prac4_2
1에서 40000까지의 스레드가 합친 sum 변수의 값은 500849160
$

1에서 40000까지의 합은 문제 1의 결과에 따라 800020000인데, 앞의 결과 화면에는 80020000가 출력되지 않는다. 여러 번 실행해도 계속 다른 값이 출력된다. 그 이유는 무엇인지 나름대로 설명해보라.

#include <pthread.h> // pthread 라이브러리를 사용하기 위해 필요한 헤더 파일 
#include <stdio.h>
#include <stdlib.h>

void* runner(void *param); // 스레드로 작동할 코드(함수) 
int total = 0; // main 스레드와 runner가 공유하는 전역 변수  
int sum = 0;
int main() {
        pthread_t tid1; //  스레드의 id를 저장할 정수형 변수 
        pthread_t tid2; //  스레드의 id를 저장할 정수형 변수 
        pthread_t tid3; //  스레드의 id를 저장할 정수형 변수 
        pthread_t tid4; //  스레드의 id를 저장할 정수형 변수 
        pthread_attr_t attr; // 스레드 정보를 담을 구조체 

        pthread_attr_init(&attr); // 디폴트 값으로 attr 초기화 
        pthread_create(&tid1, &attr, runner, "1"); // runner 스레드 생성
        pthread_create(&tid2, &attr, runner, "10001"); // runner 스레드 생성 
        pthread_create(&tid3, &attr, runner, "20001"); // runner 스레드 생성 
        pthread_create(&tid4, &attr, runner, "30001"); // runner 스레드 생성 
	// 스레드가 생성된 수 커널에 의해 언젠가 스케줄되어 실행 
        pthread_join(tid1, NULL); // tid 번호의 스레드 종료를 기다림
        pthread_join(tid2, NULL); // tid 번호의 스레드 종료를 기다림 
        pthread_join(tid3, NULL); // tid 번호의 스레드 종료를 기다림 
        pthread_join(tid4, NULL); // tid 번호의 스레드 종료를 기다림 
	printf("1에서 40000까지 4개의 스레드가 계산한 총 합은 %d\n", sum);
}

void* runner(void *param) { // param에 "값" 전달 
        int to =  atoi(param); // to = "값"
        total = 0;
        for (int i = to; i <= to + 9999; i++) { // to에서 to+9999까지 합계산 
            sum += i;
        }
}

스케줄링에 의해 4개의 스레드가 실행되는 순서가 계속 달라지기 때문에 매번 값이 달라진다.