Fork() 및 Exec() 시스템 콜
- 다중 스레드 프로그램에서는 fork()와 exec()의 의미가 달라질 수 있다.
- fork()에 대한 질문 - 한 프로그램의 스레드가 fork()를 호출했을 때
-
- 새로운 프로세스는 모든 스레드를 복제해야 하는가?
- 한 개의 스레드만 가지는 프로세스여야 하는가?
- 몇몇 UNIX 기종은 두 가지 버전의 fork()를 다 제공
- 두 버전 중 어느 쪽을 택할 것인지는 응용프로그램에 달려 있음
-
- exec() 시스템 콜을 부를 때
- exec()의 매개변수로 지정된 프로그램이 모든 스레드를 포함한 전체 프로세스를 대체
- fork()를 부르자마자 다시 exec를 부르면 모든 스레드를 복제해서 만들어주는 것은 불필요
← exec에서 지정한 프로그램이 곧 모든 것을 다시 대체할 것이기 때문
- 이 경우 fork() 시스템 콜을 호출한 스레드만 복사해 주는 것이 적절
- 이 경우 fork() 시스템 콜을 호출한 스레드만 복사해 주는 것이 적절
- 새 프로세스가 fork() 후 exec를 하지 않는 경우
→ 새 프로세스는 모든 스레드를 복제해야 함
신호처리(Signal Handling)
- 신호는 UNIX에서 프로세스가 어떤 이벤트가 일어났음을 알려주기 위해 사용
- 신호는 알려줄 이벤트의 근원지나 이유에 따라 동기식/비동기식으로 전달
- 동기식 신호
- 신호를 발생시킨 연산을 수행한 동일한 프로세스에 전달 ← 동기식인 이유
- 예 : 불법적인 메모리 접근, 0으로 나누기 → 이러한 행동을 하면 신호가 발생
- 동기식 신호
- 비동기식 신호
- 신호가 실행 중인 프로세스 외부로부터 발생하면 비동기식으로 전달받음
- 예 :
Control+C
같은 프로세스 강제 종료, 타이머가 만료되는 경우 - 비동기식 신호는 통상 다른 프로세스에 전달됨
신호의 전달 형태
- 신호는 특정 이벤트가 일어나야 생성
- 생성된 신호된 프로세스가 전달된다.
- 신호가 전달되면 반드시 처리되어야 한다.
- 신호는 다른 방식으로 처리될 수 있다.
- 일부 신호는 무시될 수 있지만 다른 신호는 프로그램을 종료하여 처리된다.
- 다른 신호 → 불법 메모리 액세스
- 일부 신호는 무시될 수 있지만 다른 신호는 프로그램을 종료하여 처리된다.
디폴트 신호 처리기와 사용자 정의 처리기
- 모든 신호는 디폴트 처리기, 사용자 처리기 둘 중 하나로 처리됨
디폴트 처리기
: 모든 신호마다 커널이 실행시킴사용자 정의 처리기
: 신호를 처리하기 위하여 호출됨- 디폴트 처리기는 사용자 정의 처리기에 의해 대체될 수 있다.
다중 스레드 프로그램에서의 신호처리
- 어느 스레드에 신호를 전달해야 하는가? - 4가지 선택이 존재
- 신호가 적용될 스레드에게 전달한다.
- 모든 스레드에 전달한다.
- 몇몇 스레드들에만 선택적으로 전달한다.
- 특정 스레드가 모든 신호를 전달받도록 지정한다.
신호 전달하는 방법 - 신호의 유형에 따라 다름
- 동기식 신호는 그 신호를 호출한 스레드에 전달해야 함
→ 다른 스레드에 전달되면 안 됨 - 비동기식 신호의 경우 → 명확하지 않음
Control+C
같은 비동기식 신호는 그 프로세스 내 모든 스레드에게 전달되어야 함- UNIX -
kill(pid_t pid, int signal)
- 함수는 특정 신호가 전달될 프로세스(pid)를 지정한다.
- 때론 비동기식 신호를 봉쇄하지 않고 있는 스레드들에게만 신호를 전달해야 할 수도 있음
- 다중 스레드 UNIX는 스레드를 받아들일 신호와 봉쇄할 신호를 지정할 선택권을 부여
- 다중 스레드 UNIX는 스레드를 받아들일 신호와 봉쇄할 신호를 지정할 선택권을 부여
- UNIX -
- Windows - 비동기식 프로시저 호출(APC)
- 신호를 명시적으로 지원하지는 않지만 APC를 통해 대리 실행 가능
- APC는 사용자 스레드가 특정 이벤트의 발생을 전달받았을 때,
호출될 함수를 지정할 수 있게 한다.
- UNIX의 비동기식 신호와 유사
- 차이점 : APC는 프로세스에 전달되는 것이 아니라 특정 스레드에게 전달됨으로 더 간단
스레드 취소
개념
- 스레드가 끝나기 전에 스레드를 강제종료시키는 작업을 일컫는 말
- 예시
- 여러 스레드가 데이터베이스를 병렬로 검색하다가
한 스레드가 결과를 찾으면 나머지 스레드는 취소 가능
- 여러 스레드가 데이터베이스를 병렬로 검색하다가
- 웹브라우저에서 사용자가 웹페이지를 더는 적재하지 않기 위해 stop버튼을 클릭할 경우
- 종종 웹페이지는 여러 스레드들을 사용하여 적재됨
→ 사용자가 stop을 누르면 웹 페이지를 가져오던 모든 스레드가 취소
- 종종 웹페이지는 여러 스레드들을 사용하여 적재됨
목적 스레드(target thread)
목적 스레드
: 취소되어야 할 스레드- 2가지 방식으로 발생 가능
비동기식 취소(asynchronous cancellation)
- 한 스레드가 즉시 목적 스레드를 강제 종료 시킨다.
지연 취소(deferred cancellation)
- 목적 스레드가 주기적으로 자신이 강제 종료 되어야 할지를 점검
- 이 경우 목적 스레드가 안전하게(절차적으로) 강제종료될 수 있는 기회가 만들어짐
스레드 취소 - 할당된 자원 문제
- 스레드 취소를 어렵게 만드는 것은 취소 스레드들에게 할당된 자원 문제
- 다른 스레드와 공유하는 자료구조를 갱신하는 도중에 취소 요청이 와도 문제가 됨
- 비동기식 취소의 경우 더 심각
- 비동기식 취소에 의해 필요한 시스템 자원을 다 사용가능한 상태로 만들지 못할 수 있음
- 원래 운영체제는 취소된 스레드로부터 시스템자원을 회수할 수 있음
- 원래 운영체제는 취소된 스레드로부터 시스템자원을 회수할 수 있음
- 비동기식 취소에 의해 필요한 시스템 자원을 다 사용가능한 상태로 만들지 못할 수 있음
- 비동기식 취소의 경우 더 심각
- 지연 취소의 경우에는 한 스레드가 목적 스레드를 취소해야 한다고 표시해야 함
→ 하지만 실제 취소는 목적 스레드가 취소 여부를 결정하기 위한 플래그 검사 이후에 일어남
- 스레드는 자신의 취소되어도 안전하다고 판단되는 시점에서 취소 여부를 검사할 수 있음
Pthread에서 스레드 취소 - pthread_cancel()
- 목적 스레드의 식별자가 이 함수의 매개변수로 전달
- pthread_cancel()을 호출하면 대상 스레드를 취소하라는 요청만 표시
- 하지만, 실제 취소는 요청을 처리하기 위해 대상 스레드가 설정되는 방식에 달려 있음
- 대상 스레드가 최종적으로 취소되면 취소 스레드의
pthread_join() 호출이
반환
- Pthread는 3가지 취소 모드를 지원 ← API를 사용하여 취소 상태 및 유형 설정 가능
- 취소 비활성화(Off)
- 스레드를 취소 불가능
- 스레드는 나중에 취소를 활성화하고 요청을 응답할 수 있음
← 취소 요청은 계속 보류 상태로 유지되기 때문
- 취소 비활성화(Off)
- Pthreads는 스레드가 취소를 활성 또는 비활성하는 것을 허용
- 기본 취소 유형은 지연 취소 → 스레드가 취소점에 도달한 경우에만 취소가 발생
- 취소점의 예
- POSIX 및 표준 C 라이브러리에서 대부분의 블로킹 시스템 콜
- Linux 시스템에서 man pthreads 명령을 호출할 때 나열
- read() 시스템 콜은 read()에서 입력을 기다리는 동안
봉쇄된 스레드의 취소를 허용하는 취소점
- read() 시스템 콜은 read()에서 입력을 기다리는 동안
- 취소점의 예
- 취소점을 설정하는 기법
pthread_testcancel() 함수
호출
- 취소 요청이 보류 중인 것으로 확인
→pthread_testcancel()
호출이 복귀되지 않고 스레드가 종료된다. - 취소 요청이 보류 중이 아닌 것으로 확인
→ 함수 호출이 복귀되고 스레드가 계속 실행됨
- 취소 요청이 보류 중인 것으로 확인
- 스레드가 취소될 때 정리 핸들러(clean handler)를 함수가 호출되게 할 수 있다.
- 해당 기능을 사용하면 스레드가 종료되기 전에 스레드에 있는 모든 자원을 해제할 수 있음
스레드-로컬 저장장치(Thread-Local Storage)
스레드-로컬 저장장치(TLS)
: 각 스레드 자신만 접근할 수 있는 데이터- 상황에 따라서는 각 스레드가 자기만 액세스 할 수 있는 데이터를 가져야 할 필요도 있음
- 예 : 트랜잭션 처리 시스템에서 각 트랜잭션을 독립된 스레드가 처리할 때
- 스레드마다 고유한 식별자를 연관시키기 위해서 스레드 국지 저장소가 있어야 함
- 예 : 트랜잭션 처리 시스템에서 각 트랜잭션을 독립된 스레드가 처리할 때
- 상황에 따라서는 각 스레드가 자기만 액세스 할 수 있는 데이터를 가져야 할 필요도 있음
TLS의 특징
- TLS는 전체 함수 호출에 걸쳐 보인다.
- cf. 지역 변수는 하나의 함수가 호출되는 동안에만 보임
- cf. 지역 변수는 하나의 함수가 호출되는 동안에만 보임
- 개발자가 스레드 생성 과정에 대해 제어할 수 없는 경우 다른 방법이 필요
- 스레드 생상 과정에 대해 제어할 수 없는 경우 → 스레드 풀과 같은 암묵적 기법
- 스레드 생상 과정에 대해 제어할 수 없는 경우 → 스레드 풀과 같은 암묵적 기법
- 어떤 면에서 TLS는 정적 데이터와 유사
- 차이점 : TLS 데이터는 스레드마다 고유함
- 차이점 : TLS 데이터는 스레드마다 고유함
- 대부분의 스레드 라이브러리 및 컴파일러는 TLS를 지원
- Java
ThreadLocal <T>
객체에 대한 set() 및 get() 메서드와 함께ThreadLocal <T>
클래스 제공
- Java
- Pthread
- pthread_key_t 유형이 포함되어 있으며 각 스레드에 고유한 키를 제공
→ 그런 다음 키를 사용하여 TLS 데이터에 접근 가능
- pthread_key_t 유형이 포함되어 있으며 각 스레드에 고유한 키를 제공
스케줄러 액티베이션
- 스레드 라이브러리와 커널의 통신 문제 ← 다중 스레드 프로그램에서 고려해야 할 문제
- 다대다 및 두 수준 모델에서 반드시 해결해야 하는 문제
- 다대다 및 두 수준 모델에서 반드시 해결해야 하는 문제
- 이러한 통신의 조정은 응용프로그램이 최고의 성능을 보이도록 보장하기 위해
커널 스레드의 수를 동적으로 조절하는 것을 가능하게 함
경량 프로세스(LWP)
- 사용자와 커널 스레드 사이에 중간에 두는 자료구조
- 다대다 또는 두 수준 모델을 구현하는 많은 시스템이 많이 사용
- 다대다 또는 두 수준 모델을 구현하는 많은 시스템이 많이 사용
- 사용자 스레드 라이브러리에 LWP 방식
- 응용이 사용자 스레드를 수행하기 위하여 스케줄 할 가상 처리기처럼 보임
- 각 LWP는 하나의 커널 스레드에 부속되어 있음
- 물리 처리기에서 스케줄 하는 커널 스레드(대상)
- 물리 처리기에서 스케줄 하는 커널 스레드(대상)
- 입출력이 완료될 때까지 커널스레드가 봉쇄되면 LWP도 같은 봉쇄됨
→ 이 연관에 따라 LWP에 부속된 사용자 수준 스레드도 같이 봉쇄
앱에서의 LWP
- 앱의 효율을 위해 임의의 개수의 LWP가 필요할 수도 있음
- CPU 중심 앱이 아닌 입출력 중심 앱은 여러 개의 LWP가 필요할 수 있음
← 통상 동시에 발생하는 봉쇄형 시스템 콜마다 하나의 LWP가 필요하다.
- 예
- 서로 다른 5개의 파일 읽기 요청이 발생
→ 모든 LWP가 입출력 완료를 커널 안에서 기다릴 수 있기 때문에 5개의 LWP 필요
- 서로 다른 5개의 파일 읽기 요청이 발생
- 예
- CPU 중심 앱이 아닌 입출력 중심 앱은 여러 개의 LWP가 필요할 수 있음
스케줄러 액티베이션의 동작과정
- 스케줄러 액티베이션 ← 사용자 스레드 라이브러리와 커널 스레드 간의 통신 방법
- upcall
- 커널이 앱에 가상처리기(LWP)의 집합을 제공 및 특정 이벤트에 대해 알려주고,
앱은 사용자 스레드를 가용한 가상처리기로 스케줄 하는 과정을 일컫는 말
- 커널이 앱에 가상처리기(LWP)의 집합을 제공 및 특정 이벤트에 대해 알려주고,
- upcall은 스레드 라이브러리의 upcall 처리기에 의해 처리
- upcall 처리기는 가상 처리기상에서 실행되어야 함
- upcall 처리기는 가상 처리기상에서 실행되어야 함
- 응용 스레드가 봉쇄하려고 할 때 발생하는 upcall 이벤트 과정
- 커널은 스레드가 봉쇄하려고 한다는 사실과 그 스레드의 식별자를 알려주는 upcall 한다.
- 이후 커널은 새로운 가상 처리기에 응용(앱)을 할당
- 앱은 이 새로운 가상 처리기상에서 upcall 처리기를 수행하고,
봉쇄 스레드의 상태를 저장하고 이 스레드가 실행 중이던 가상 처리기를 반환
또한, upcall 처리기는 새로운 가상 처리기에서 실행 가능한 다른 스레드를 스케줄 - 봉쇄 스레드가 기다리던 이벤트가 발생하면 커널은 이전에 봉쇄되었던 스레드가
이제 실행할 수 있다는 사실을 알려주는 또 다른 upcall을 스레드 라이브러리에 한다. - 이 이벤트를 처리하는 upcall 처리기도 가상 처리기가 필요하고, 커널은 새로운 가상 처리기를
할당하거나 사용자 스레드 하나를 선점하여 그 처리기에서 upcall 처리기를 실행 - 봉쇄가 풀린 스레드를 실행 가능 상태로 표시한 후에
앱은 가용한 가상 처리기상에서 다른 실행 가능한 스레드를 실행