시그널 받았을 때 처리할 수 있는 함수 지정 가능하게 함
signal, sigset은 시그널 핸들러만 지정 가능, sigaction은 다양하게 시그널 제어 가능함
sigaction 구조체
<sys/signal.h>에 정의되어 있음
시그널 처리 위한 시그널 핸들러 주소, 핸들러 수행하는 동안 블록될 시그널, 추가적인 기능 설정 가능한 플래그로 구성됨
struct sigaction{
int sa_flags;
union {
void (*sa_hadnler)();
void (*sa_sigaction)(int, siginfo_t *, void *);
} _funcptr;
sigset_t sa_mask;
}
- sa_falgs : 시그널 전달 방법 수정할 플래그 지정
- 논리 OR 연산으로 연결해서 지정함
- 안쓰면 0으로 두기
SA_ONSTACK (0x00000001) |
시그널 처리할 프로세스에 sigaltstack 시스템 호출로 생성한 대체 시그널 스텍이 있을 때에만 대체 스택에서 시그널 처리 아니면 시그널은 일반 스택에서 처리 |
SA_RESETHAND (0x00000002) |
시그널 기본 처리 방법이 SIG_DFS로 재설정되고 시그널 처리되는 동안 시그널 블록 X |
SA_NODEFER (0x00000004) |
시그널 처리되는 동안 유닉스 커널에서 해당 시그널을 자동으로 블락 못함 |
SA_RESTART (0x00000004) |
시스템은 시그널 핸들러에 의해 중지된 기능을 재시작하게 함 |
SA_SIGINFO (0x00000008) |
설정 안된 상태에서 시그널 받으면 시그널 번호(sig 인자)만 시그널 핸들러로 전달됨 설정하고 시그널 받으면 시그널 번호 외에 추가 인자 2개가 핸들러로 전달됨. - 두 번째 인자가 NULL 아닐 경우 : 시그널 발생한 이유가 저장된 siginfo_t 구조체 가리킴 - 세 번째 인자 : 시그널 전달될 때 시그널 받는 프로세스 상태 나타내는 ucontext_t 구조체 가리킴 |
SA_NOCLDWAIT (0x00010000) |
시그널이 SIGCHLD면 시스템은 자식 프로세스 종료될 때 좀비 프로세스 만들지 않음 |
SA_NOCLDSTOP (0x00020000) |
시그널 SIGCHLD면 자식 프로세스가 중지 or 재시작할 때 부모 프로세스에 SIGCHLD 시그널 전달 X |
- sa_handler / sa_sigaction : 시그널 처리 위한 동작 지정
- 메모리가 중첩됨
- sa_flag에 SA_SIGINFO 설정 X -> sa_handler에 시그널 처리할 동작 지정
설정 O -> sa_sigaction 멤버 사용
- sa_mask : 시그널 핸들러 수행되는 동안 블록될 시그널 지정
- 시그널 핸들러 시작되어 시그널 전달할 때 이미 블록된 시그널 집합에 sa_mask로 지정한 시그널 집합 추가
- sa_flags에 SA_NODEFER 설정 X -> 시그널 핸들러 호출하게 한 시그널도 블록됨
sigaction 함수
시그널 핸들러 지정, 플래그 설정해서 시그널 처리 과정 제어 가능, 시그널 핸들러 수행되는 동안 다른 시그널 블록 가능
#include <signal.h>
int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);
void handler(int signo){
psignal(signo, "received signal: ");
sleep(5);
printf("in signal handler, after sleep\n");
}
//핸들러 처리 후 바로 블락된 시그널 처리됨
int main(){
struct sigaction act;
sigemptyset(&act.sa_mask); //sa_mask 초기화
sigaddset(&act.sa_mask, SIGQUIT); //SIGQUIT block
act.sa_flags = 0;
//act.sa_flags = SA_RESETHAND;
//시그널 핸들러 한번 호출된 후에 기본처리 방법으로 재설정됨 (process 종료)
act.sa_handler = handler;
sigaction(SIGINT, &act, (struct sigaction *)NULL);
fprintf(stderr, "Input SIGINT: ");
pause();
fprintf(stderr, "After Signal Handler\n"); //실행 안됨
}
/* 실행 결과
$^c ^\
Received signal ::interrupt
in signal handler, After Sleep
끝(Quit)(코어 덤프)
*/
- sig : 처리할 시그널
- SIGKILL, SIGSTOP 제외 모든 시그널 사용 가능
- act : 시그널 처리할 방법 지정한 구조체 주소 (NULL 가능)
- oact : 기존에 시그널 처리하던 방법 저장할 구조체 주소
- 성공하면 0 실패하면 -1 리턴
시그널 발생 원인 검색
sa_flags 에 SA_SIGINFO 플래그 설정
- 시그널 발생 원인 알 수 있음
- sigaction 구조체에서 핸들러 지정할 때 sa_handler 대신 sa_sigaction 사용
- 시그널 핸들러 형식이 인자 세 개를 받는 형태로 정의됨
void handler(int sig, siginfo_t *sop, ucontext_t *ucp);- sig: 시그널 핸들러 호출할 시그널
- sip: 시그널이 발생한 원인 담은 siginof_t 구조체 포인터
- ucp: 시그널 전달될 때 시그널 받는 프로세스 내부 상태 나타내는 ucontext_t 구조체 포인터
siginfo_t 구조체
typedef struct{
int si_signo; //시스템에서 제공하는 시그널 번호 저장
int si_errno; //0 또는 시그널 관련 오류 번호 저장
int si_code; //시그널 발생 원인 정의하는 코드 저장함
//SI_NOINFO면 si_signo만 의미 있고 siginfo_t 구조체 나머지 멤버는 사용 X
union sigval si_value;
union{
...
}__data; //시그널 종류 따라 값 저장됨
} siginfo_t;
signal 발생 원인 코드
- 시스템 프로세스에 의한 시그널 발생 원인 코드
- si_code 값이 양수면 시스템이 시그널 생성
- man -s 3HEAD siginfo.h 명령으로 시그널 발생 원인 나타낸 코드 볼 수 있음
- 사용자 프로세스에 의한 시그널 발생 원인 코드
- <sys/siginfo.h>에 정의됨, man -s 3HEAD siginfo.h 명령으로 확인 가능
SI_USER (주로 얘로 테스트함) |
0 | kill, sigsend, raise, abort 함수가 보냄 |
SI_LWP | -1 | _lwp_kill 함수가 시그널 보냄 |
SI_QUEUE | -2 | sigqueue 함수가 보냄 |
SI_TIMER | -3 | timer_settime 함수가 생성한 타이머가 만료되어 시그널 보냄 |
SI_ASYNCIO | -4 | 비동기 입출력 요청 완료돼서 보냄 |
SI_MESGQ | -5 | 빈 메시지 큐에 메시지 도착했음 알리는 시그널 보냄 |
psiginfo - 시그널 발생 원인 출력
#include <siginfo.h>
void psiginfo(siginfo_t *pinfo, char *s);
void handler(int signo, siginfo_t *sf, ucontext_t *uc){ //추가적인 인자 2개 받음
psiginfo(sf, "received signal");
printf("si_code :%d\n",sf->si_code);
}
int main(){
struct sigaction act;
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = (void (*)(int, siginfo_t*,void*))handler;
sigemptyset(&act.sa_mask); //블락되는 시그널 없음
sigaction(SIGUSR1, &act, (struct sigaction *)NULL); //유저 핸들러
pause();
}
/*
$test& //&: 백그라운드 실행
[1] 2020
$kill -USR1 2020
received signal: :user signal 1 (from process 2020)
*/
si_code : 0