본문 바로가기
CS/운영체제

[Chapter 4. 스레드] 스레드와 관련한 문제들

by 베어 그릴스 2022. 8. 21.
본 정리는 운영체제(Operating System: Concepts) 9th edition과 22학년도 1학기 건국대학교 운영체제 수업을 바탕으로 하고 있습니다.

 

이 절에선 다중 스레드 프로그램을 설계할 때 고려해야할 문제들에 대하여 정리한다.

 

 

fork() 및 exec() 시스템 호출


앞서 fork와 exec을 통하여 자식 프로세스를 만드는 방법에 대하여 알아보았다.

참고: https://developbear.tistory.com/101?category=1027028 

 

[Chapter 3. 프로세스] 프로세스의 생성과 종료

본 정리는 운영체제(Operating System: Concepts) 9th edition과 22학년도 1학기 건국대학교 운영체제 수업을 바탕으로 하고 있습니다. 프로세스는 한 프로세스에 의해 새로 생성될 수 있으며, 생성된 프로

developbear.tistory.com

그렇다면 프로세스 내부에서 실행한 스레드들은 자식 프로세스에선 어떻게 되는 것일까?

 

새로운 프로세스는 모든 스레드를 복제해서 가져야 하는지 아니면 하나의 스레드만 가지는 프로세스이어야 하는가?

 

몇몇 UNIX 기종은 두 가지 버전 fork()를 모두 제공한다. 

1. 모든 스레드를 복사한다.

2. fork()를 호출한 스레드만 복사한다.

 

위 둘 중 어떤 것을 선택할지는 응용프로그램(코드)의 선택이다.

 

exec()은 보통 fork 후에 해당 자식 프로세스가 exec의 매개변수로 지정된 프로그램을 실행시킨다. 이러한 경우에는 모든 스레드가 복제될 필요가 없기 때문에 fork()한 스레드만 복사해주는 것이 적절하고, 단순히 새로운 자식 프로세스를 생성하는 것이라면, 모든 스레드를 복사하는 것이 적절하다.

 

 

신호처리


신호는 UNIX에서 프로세스에게 어떤 사건이 일어났음을 알려주기 위해 사용된다.

 

신호는 알려줄 사건의 근원지나 이유에 따라 동기식 또는 비동기식으로 전달될 수 있다.

 

동기식 신호 : 불법적인 메모리 접근, 0으로 나누기 등 소프트웨어적인 문제. 신호를 발생시킨 연산을 수행한 동일한 프로세스에게 전달되므로 동기식이라고 부른다

 

비동기식 신호: 프로그램 실행 중 외부에서 ctrl C 등의 특수 키를 눌러 프로그램을 종료 시키는 경우

 

신호는 다음과 같은 형태로 전달된다,

 

1. 신호는 특정 사건이 일어나야 생성된다.

2. 생성된 신호가 프로세스에게 전달된다.

3. 신호가 전달되면 반드시 처리되어야만 한다.

 

 

모든 신호는 둘 중 하나의 처리기에 의해 처리된다

 

1. 디폴트 신호 처리기 : 모든 커널이 실행시킨다.

2. 사용자 정의 처리기 : 디폴트 신호 처리기를 대체하여 신호를 처리할 수 있다

 

 

단일 스레드 프로그램에서의 신호 처리는 간단하다. 신호는 항상 프로세스에게 전달 된다.

 

그러나 다중 스레드에선 신호가 모든 스레드에 가야하는지 하나의 스레드에 가야하는지 신호 처리가 복잡해진다.

 

어느 스레드에게 신호를 전달해야할까?

 

일반적으로 다음과 같은 선택이 존재한다.

 

1. 신호가 적용될 스레드에게 전달

2. 모든 스레드에게 전달

3. 몇몇 스레드들에게만 선택적으로 전달

4. 특정 스레드가 모든 신호를 전달받도록 지정

 

ctrl C 같은 키를 눌러 프로세스를 강제 종료 시킬 경우 프로세스 내 모든 스레드에게 전달되어야 한다.

이런 신호를 전달하는 데 사용되는 표준 UNIX 함수는 다음과 같다.

kill(pid_t pid,int signal)

 

대부분의 다중 스레드 UNIX는 스레드에게 받아들일 신호와 봉쇄할 신호를 지정할 수 있는 선택권을 준다. 따라서 어떤 경우에는 비동기식 신호를 봉쇄하지 않고 있는 스레드들에게만 신호를 전달해야 할 수 있다. 하지만 신호는 한번만 처리되어야 하기 때문에 그 신호를 봉쇄하지 않고 있는 첫 번째 스레드에게만 신호가 전달된다.

 

POSIX Pthreads는 tid로 지정된 스레드에게만 전달이 되도록 허용하는 다음과 같은 함수를 제공한다.

pthread_kill(pthread_t tid, int signal)

 

취소 (Cancellation)


스레드를 취소한다는 것은 해당 스레드가 끝나기 전에 그것을 강제 종료시키는 작업을 말한다.

이때 취소되어야 하는 스레드는 목적 스레드(target thread)라고 부른다.

 

스레드 취소 요청 시 해당 목적 스레드들에게 할당되었던 자원에 대하여 유의하여야 한다.

만약 목적 스레드에 공유 자원이 있었다면 이를 바로 강제 종료시켜서는 안 될 것이다.

 

목적 스레드를 취소하는 방식에는 두 가지가 존재한다.

 

비동기식 취소

한 스레드가 목적 스레드를 즉시 강제 종료시켜 버린다.

이는 필요한 시스템 자원을 전부 다 사용 가능한 상태로 만들지 못할 수 있기 때문에 권장하지 않는 방식이다.

 

지연 취소 (Deferred cancellation)

목적 스레드가 자신이 강제 종료되어야 할지를 주기적으로 점검한다.

스레드가 취소 가능한지 불가능한지를 결정할 수 있게 해주는 것이다.

 

* 취소된 스레드들에게 할당된 자원들의 문제 때문에 비동기식 취소는 권장되지 않는다. 취소 상태인지 확인하고 취소하는 지연 취소를 보통 사용한다.

 

 

스레드 국지 저장소 (Thread-Local Storage)


한 프로세스에 속한 스레드들은 해당 프로세스의 데이터를 모두 공유한다.

하지만 각 스레드가 자기만 액세스 할 수 있는 데이터를 가져야 할 수 있으며, 이 데이터들은 스레드 국지 저장소(TLS)라고 부른다.

 

Windows에서는 스레드 환경 블록(thread environment block, TEB)가 존재한다.
이는 사용자 모드에서 실행될 때 접근되는 사용자 공간 자료구조로,

스레드 식별자, 사용자 모드 스택 및 스레드 국지 저장소를 저장하기 위한 배열을 가지고 있다.

 

* ETHREAD(executive thread block) : 실행 스레드 블록

* KTHREAD(kernel thread block) : 커널 스레드 블록

 

TLS와 지역변수를 혼동하기 쉬운데 이는 다르다. 지역 변수는 하나의 함수가 호출되는 동안에만 보이는 반면 TLS는 전체 함수 호출에 걸쳐 보인다.

 

경량 프로세스 (Light Weight Process, LWP)

다대다, 다대일 및 두 수준 모델에서 스레드 라이브러리와 커널이 통신을 하기 위해서

경랑 프로세스(LWP)라는 자료 구조를 사용자 스레드와 커널 스레드 사이에 둔다.

 

 

각 LWP는 하나의 커널 스레드에 부속되어 있고, 물리 처리기에서 스케줄을 하는 대상은 그 커널 스레드이다.

 

만약 한 커널 스레드가 봉쇄된다면 그에 부속되어 있는 LWP도 같이 봉쇄되고, 그것에 따라 LWP에 부속되어 있는 사용자 스레드도 봉쇄된다.