[시그널] 시그널 집합(signal set) 처리.
<시그널>
규모가 큰 소프트웨어를 만들 때 여러 개의 협력하는 프로세스들을 구성하여
서로 정보를 주고 받도록 하는 프로세스 간 통신 기법 중의 하나.
=> 프로그램 실행 시간이 오래 걸리거나, 잘못되었을 때, 리눅스에서는 Ctrl_C 같은 인터럽트 키를 이용하여 명령을 종료 시킴.
: 키보드로 입력한 Ctrl_C 키는 커널에서 감지 -> 해당 프로세서에 시그널을 보냄.
=> 또는 백그라운드에서 kill 을 입력하면, 실행한 작업을 강제로 종료시킴.
예>
$ ./a.out &
[1] 2548 이 출력됐다면,
$ kill 2548
=> 유닉스 , 리눅스에서 사용되는 시그널은 30 여가지가 있음. 시그널은 signal.h 에서 제공.
시그널을 다룰 때 시그널 집합으로 다루는 것이 편리함.
sigemptyset(), sigfillset(), sigaddset() sigdelset() : 시그널 집합(signal set) 함수들
시그널은 여러가지 종류가 있으며. 그것들을 모두 포함할 수 있는 sigset_t 이라는 시그널 집합 데이터형이 있다
sigfillset() : 시그널을 모두 포함하는 세트 생성
#include <signal.h>
int sigfillset(sigset_t *set);
시그널을 모두 포함하라는 함수로 간단히 비유하자면 여러가지 신호들이 있는데 특정신호만 감지하는 것이 아니라
모든 신호들을 감지하겠다는 의미나 마찬가지다.
sigaction() : 시그널 처리 함수
(예전에는 signal() 함수 사용하였음 : 불안정적임)
#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
프로세스가 특정 시그널에 대하여 수행할 행동을 지정하도록 함. (세가지 중 하나)
1. 프로세스는 종료하고 코어 덤프함
2. 내가 지정한 시그널을 무시함
3. 시그널 핸들러에 내가 지정한 함수를 실행함
성공 시 0, 실패하면 -1 리턴
내가 지정한 특정 신호에 대해서. 어떻게 처리할지 결정하는 함수이다.
그 신호를 무시할 수도 있고, 그 신호가 오면. 내가 미리 만들어둔 함수가 실행되게 할 수도 있다.
sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
signo가 int 타입이지만 signal.h에 문자로 이미 정의 되어 있기도 하다(시그널 명). 따라서 시그널 이름도 가능하다.
act와 oact는 똑같은 sigaction 구조체인데. act 자리는 그 안에 sa_handler의 함수를 실행하는 것이며,
oact자리는 그 전까지 신호(signo)에 대해 처리했던 방식을 저장해 준다. 필요 없다면 NULL로 작성한다.
sigaction 구조체이다, 이해하지 못한 부분은 sa_sigaction 이라는 함수포인터가 있는데
sa_flags 값이 sa_siginfo 이면 sa_handler 대신 sa_sigaction 의 함수가 작동 된다고 함. 이해는 못했음.
sigset_t sa_mask는 시그널 처리 함수가 실행되는 동안 블로킹 될 시그널을 설정 한다고 한다.
sa_sigaction 함수 포인터의 매개 변수로 있는 siginfo_t 구조체.
[sinaction(), sigfillset() 를 이용한 내가 원하는 함수 동작시키기.]
handler 함수. 사실 처음에 살짝 속을뻔 했다.
왠지.. 이름이 좀 그럴싸 해보이는게. 미리 정해진 함수인줄 알았다.
sigaction 이라는 구조체를 act 라는 이름으로 만들었다. (sigaction 구조체는 이미 정해진 것)
sa_handler 는 함수포인터로 내가 만든 함수의 주소를 주면 되는 것이다. (함수의 이름은 함수의 주소와 같다.)
단, 매개변수가 같아야 하므로 (int) 여야 한다. 실제적으로 내가 원하는 기능을 이 함수에 구현하는 것은 아니고,
형식에 맞는 함수를 만들어서 이 함수가 작동되게 하고, 그 함수 안에서 내가 원하는 함수를 호출해야 하는 거 같다.
물론 간단한 내용이라면 그냥 그 함수에서 사용해도 무관하지만.
sigfillset()의 매개변수는 *sigset_t 이다. 따라서 act 에 있는 sa_mask의 주소를 넘겨준다.
sa_mask를 모두 채웠으므로 sa_mask는 모든 시그널을 의미하게 된다.
sigcation() 을 보면 SIGINT 라는 신호가 들어오면 act 안에 있는 sa_handler의 함수를 실행하며,
함수가 실행되는 동안 sa_mask에 포함된 신호들은 모두 블로킹 된다.
마지막 NULL 부분은, sigaction 구조체를 넣어준다면 이전까지 이 신호에 대해서 처리했던 방식을 저장해 준다.
반복문을 이용해서 1초에 한번씩 출력하게 한다.
이때 SIGINT 신호를 주면 handler 이라고 만들어둔 함수의 내용이 작동 되어서 출력문이 화면에 나타난다.
SIGINT는 CTRL + C 이다. 신호이름과 번호에 관한 내용은 표를 보면 알 수 있다.
[결과화면]
위의 예제와 별로 다른 것은 없지만, sa_handler에 SIG_IGN 이 대입 되었다.
이는 미리 정해진 것으로 SIG_IGN은 신호를 무시하는 것이고, SIG_DFL은 디폴트 액션을 취하라는 것이다.
따라서 아무리 CTRL + C 를 눌러도 정지가 되지 않는다. 그냥 무시하기 때문에.
[결과 화면]
CTRL + C 가 눌러졌지만 멈추지 않는 것을 볼 수 있다.
sigemptyset(), sigfillset(), sigaddset() sigdelset() : 시그널 집합(signal set) 함수들
#include <signal.h>
int sigfillset(sigset_t *set);
시그널을 모두 포함하는 세트 생성
int sigemptyset(sigset_t *set);
빈 시그널 세트 생성. 현재 포함된 모든 시그널을 제외 시킨다.
int sigaddset(sigset_t *set, int signo);
시그널 세트에 특정 시그널을 번호로 추가. 시그널 이름으로도 가능
int sigdelset(sigset_t *set, int signo);
시그널 세트에서 특정 시그널을 번호로 삭제. 시그널 이름으로도 가능
위 함수들을 사용해서 sigset에 내가 확인 하고 싶은 신호만 추가하거나, 불필요한 신호를 뺄 수 있다.
위의 함수들과 sigaction(), sigprocmask(), sigpending 등등 함수들은 성공 시 0, 실패 시 -1을 리턴하며,
sigsuspend() 이라는 함수만 항상 -1을 리턴한다.
sigprocmask() : 어떤 프로그램이 중요한 작업을 수행하고 있을 때 특정 시그널로 부터 방해받지 않아야 할 경우 차단.
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
특정 시그널을 차단함.
sigsetjmp(), siglongjmp() : 프로세스 이전 상태 저장 및 복귀 함수들
#include <setjmp.h>
int sigsetjmp(sigjmp_buf env, int savesigs);
//현재 프로그램 상태를 저장함
void siglongjmp(sigjmp_buf env, int val);
//저장된 위치로 복귀함