쓰레드와 시그널
윤 상배
dreamyun@yahoo.co.kr
고친 과정 | ||
---|---|---|
고침 0.9 | 2004년 1월 29일 23시 | |
시그널을 이용한 쓰레드 작동/중지 제어 | ||
고침 0.8 | 2003년 10월 7일 23시 | |
최초 문서작성 |
- 차례
- 1. 쓰레드에서의 시그널 사용
-
- 1.1. 시그널을 특정 쓰레드로 보내기
-
- 1.1.1. 간단 예제
- 1.2. 쓰레드간 시그널 전송
-
- 1.2.1. 다른 쓰레드로 시그널 전송
- 1.2.2. 시그널 받기
- 1.2.3. 예제
- 1.2.4. 시그널을 이용한 쓰레드 작동 제어
- 1.2.2. 시그널 받기
- 1.2.1. 다른 쓰레드로 시그널 전송
- 1.3. 운영체제별 차이점
1. 쓰레드에서의 시그널 사용
쓰레드에서의 시그널 사용은 시그널에 대한 기본적인 이해만 가지고 있다면 약간의 응용으로 충분히 해결할 수 있는 문제이긴 하지만 범 유닉스적으로 응용하고자 한다면(특히 리눅스가 포함된다면) 운영체제간 신경써줘야할 문제가 있다. 이번장에서는 쓰레드에서의 시그널을 이용하는 방법과 운영체제가 다름으로 인해 발생할 수 있는 문제들에 대해서 알아보도록 하겠다.
1.1. 시그널을 특정 쓰레드로 보내기
쓰레드에서 시그널은 서로 공유된다는걸 알고 있을 것이다. 문제는 공유된다는 점인데 만약 프로세스에 시그널을 보낼 경우 해당 프로세스에서 생성된 모든 쓰레드에 시그널이 전달이 되게 된다. 이것은 우리가 원하는게 아니다.
우리가 원하는 것은 특정 쓰레드에서만 시그널을 받도록 하는 것이다. 이러한 작업을 위해서 우리는 시그널 마스크를 사용한다. 시그널 마스크는 말그대로 특정 시그널에 대해서 마스크를 씌우는 것으로 해당 쓰레드에서 특정 시그널에 대해서 마스크를 씌우면 마스킹된 시그널은 해당 쓰레드로 전달되지 않는다. 이 시그널을 받기를 원하는 쓰레드에서는 이 시그널에 대한 마스크를 제거시킨다. 그러면 블럭되어 있는 시그널은 마스크가 제거된 쓰레드로 전달될 것이다. 일종의 필터기다.
위의 그림은 시그널 마스크의 작동원리를 보여준다. 메인 쓰레드에서는 SIGINT와 SIGUSR2에 대해서 시그널 마스크를 설치한다. 그리고 쓰레드 1에서는 SIGINT에 대한 마스크를 제거하고, 쓰레드 2에서는 SIGUSR2에 대한 마스크를 제거한다. 이렇게 되면 SIGINT가 메인 쓰레드에 도착했을 때 마스크 때문에 메인 쓰레드에는 도착하지 못하고 쓰레드 1로 전달될 것이다. SIGUSR2가 도착했을 경우 메인 쓰레드와 쓰레드 1에서는 마스크 때문에 전달되지 못하고 쓰레드 2로 시그널이 전달된다. 1.1.1절에서는 위의 작동원리 대로 구현된 예제 코드를 다루고 있다.이러한 쓰레드별 시그널 마스킹을 위해서 pthread는 pthread_sigmask(3)라는 함수를 제공한다.
#include |
1.1.1. 간단 예제
그럼 pthread_mask(3)를 이용한 간다한 예제를 만들어 보도록 하겠다. 코드는 여러분이 시그널과 쓰레드에 관한 최소한의 지식을 가지고 있다는 가정하에 작성될 것이며, 설명은 주석으로 대신하도록 하겠다.
예제 : th_signal.c
#include |
1.2. 쓰레드간 시그널 전송
외부의 다른 프로세스에서 시그널을 발생시키는 것 외에도 같은 프로세스에서 작동하는 쓰레드간에 시그널을 전송해야 하는 경우도 생길 것이다.
이러한 쓰레드간 시그널 전송은 여러가지 목적으로 사용할 수 있다. 일정시간마다 특정 쓰레드에 시그널을 전송하므로써 쓰레드를 깨워서 코드를 실행시키게 한다거나 네트워크 애플리케이션에서 write, read에 타임아웃을 검사하는 용도로도 사용가능 하다.
네트워크 애플리케이션에서 스레드간 시그널 전달을 통해 타임아웃을 검사한다는 생각은 좀 생소할 수도 있을것 같다. 보통은 select나 alarm을 사용할 건데, 멀티 쓰레드 프로그램의 경우 alarm(2)의 사용은 사실상 어렵다고 볼 수 있다. 여러개의 쓰레드에서 alarm(2)을 사용할 경우 단지 하나의 alarm(마지막 alarm값)만이 등록되어서 사용할 수 있기 때문이다. 그렇다면 select를 사용해야 할 건데, select대신에 전용의 시그널을 발생하는 쓰레드를 이용해서 사용할 수 있다.
read(2)를 예로 들어서 설명해 보자 read(2)를 하기전에 특정 (전역)값을 0으로 세팅하고 read를 수행한후 1로 값을 변경하도록 한다. 그리고 타임아웃 체크를 위한 쓰레드에서는 타임아웃 시간 간격으로(sleep(2)를 이용하면 된다) 이 값을 검사한다. 만약 값이 0으로 세팅되어 있는걸 확인 했는데, 다음 시간이 돌아온 뒤에도 이 값이 0이라면 read영역에서 타임아웃이 발생했다고 판단 할 수 있을 것이다. 그러면 타임아웃이 발생한 쓰레드에 시그널을 전송하도록 한다. 쓰레드에 시그널이 전송하면 인터럽트가 발생하고 read에서 빠져나오게 된다.
if (read(..) < 0) { // 만약 인터럽트로 인하여 빠져나온 거라면.. if (errno == EINTR) { ... } } |
1.2.1. 다른 쓰레드로 시그널 전송
이러한 쓰레드간 시그널 전송을 위해서 pthread_kill(3)이라는 함수가 제공된다.
#include |
1.2.2. 시그널 받기
시그널을 받는 쓰레드의 경우 동기와 비동기 두가지 방식을 통해서 받을 수 있다. 동기 방식으로 받을 경우는 sigwait(3)함수를 이용해서 시그널이 전달될 때까지 블럭되면서 기다린다.
#include |
두번째는 비동기적인 방식으로 코드 실행중에 시그널이 전달되면 인터럽트가 걸리고 시그널 핸들러가 수행되는 방식이다. 일반적인 시그널 사용방식과 동일하다.
1.2.3. 예제
sigwait(3)를 통해서 동기적으로 기다리는 것은 구현이 간단하므로 따로 다루지 않고 시그널 핸들러를 등록해서 비동기적으로 시그널을 기다리는 코드를 구현해 보도록 하겠다. 1.1.1절의 코드를 약간 수정했다.
예제 : thtoth_sig.c
#include |
1.2.4. 시그널을 이용한 쓰레드 작동 제어
쓰레드 프로그래밍을 하다보면 비동기 적으로 특정 쓰레드를 중단 시켜야 되는 경우가 발생한다. 물론 임의의 시점에서 중단된 쓰레드를 다시 작동하도록 만들어 주어야 할것이다.
다른 우회적인 몇가지 구현 방법이 있겠지만 비동기적인 처리를 위해서는 역시 시그널만한게 없는 것 같다.
#include |
1.3. 운영체제별 차이점
쓰레드의 작동방식은 운영체제별로 많은 차이를 보여줄 수 있으며, 차이점에 유의해서 프로그램을 작성해야 한다. 여기에서는 솔라리스와 리눅스를 비교해서 설명하도록 하겠다.
지금까지의 쓰레드와 시그널에 대해서 다루었던 내용은 솔라리스와 같이 하나의 프로세스에서 다중의 쓰레드를 관리하는 경우를 기준으로 했다. 그러나 리눅스의 경우 clone(2)를 통한 다중 프로세스형태로 쓰레드가 생성된다. 때문에 ps를 이용해서 확인할 경우 다중 쓰레드 프로세스임에도 불구하고 각각의 PID를 가지는 프로세스로 쓰레드가 생성되는걸 확인 할 수 있다.
이런 특징 때문에 리눅스 시스템에서 외부 프로세스에서 시그널을 특정 쓰레드로 보낼 경우에는 메인 쓰레드가 아닌 해당 쓰레드의 PID를 명시해 주어야 한다.
'Native > C' 카테고리의 다른 글
Video Capture에 대하여 (0) | 2013.10.02 |
---|---|
Packet Capture using libpcap (0) | 2013.10.02 |
간단한 c 프로그램과 cron으로 mysql 백업을 편하게... (0) | 2013.10.02 |
인자로서의 함수 (0) | 2013.10.02 |
레기드 배열 (0) | 2013.10.02 |