|
System Program/signal 2014. 2. 19. 13:43
프로세스에 가해지는 소프트웨어적인 인터럽트라고 이해하면 쉽다.
1. 시그널의 범주
전통적 또는 표준 시그널이라는 두 가지의 큰 범주로 나누어진다. 리눅스에서 표준 시그널은 1 ~ 31까지의 범위이다.
그 이외의 범위는 실시간 시그널에 해당된다.
2. 기본 동작
시그널이 프로세스에 전달되면 다음 중 하나의 기본 동작을 수행한다.
- 시그널이 무시된다. 커널에 의해 버려지고 프로세스에는 아무런 영향이 없다.
- 프로세스를 종료한다. 비 정상적 프로세스 종료라고 한다.
- 코어 덤프 파일을 생성하고 프로세스를 종료한다.
- 프로세스를 멈춘다.
- 이전에 멈췄던 프로세스의 실행을 재개한다.
3. 속성 변경
시그널의 기본 동작을 변경할 수 있는데 이를 속성 변경이라고 하며 아래 중 하나로 변경할 수 있다.
- 기본 동작 수행 (이전에 변경된 속성을 원래되로 돌리는 경우를 말함)
- 시그널을 무시한다. (종료가 기본 동작인 시그널에 유용하다)
- 시그널 핸들러를 실행한다.
* 시그널 핸들러에서 기본동작 수행과 시그널 무시는 핸들러 함수 대신에 각각 SIG_DFL과 SIG_IGN으로 핸들러 함수를 대신할 수 있다.
4. 속성 변경 예시
/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
void signal_handler(int sig)
{
if(sig == SIGINT)
printf("SIGINT is tranfered \n");
if(sig == SIGQUIT)
{
printf("SIGQUIT is tranfered \n");
exit(1);
}
}
int main(int argc, char *argv[])
{
if(signal(SIGINT, signal_handler) == SIG_ERR)
{
printf("signal error\n");
exit(1);
}
if(signal(SIGQUIT, signal_handler) == SIG_ERR)
//if(signal(SIGQUIT, SIG_DFL) == SIG_ERR)
{
printf("signal error\n");
exit(1);
}
//while(getchar() != 'q');
for(;;)
{
printf("main function is paused, PID = %d \n", getpid());
pause();
}
return 0;
}
[설명]
SIGINT과 SIGQUIT의 기본 동작을 별도의 시그널 핸들러 함수를 통하여 변경하였다.
SIGQUIT의 기본 동작은 비 정상적인 프로세스 종료를 유도하고 디버깅을 위한 코어덤프를 수행하는 것이다.
이 예제에서는 비 정상적인 종료라는 기본동작을 변경하여 정상적인 종료 동작을 하도록 강제로 변경하였다.
예제로써 적절한 것 같지는 않지만 동작성을 읽히는데 포커스를 둔다.
System Program/signal 2013. 7. 8. 13:07
1. sigaction()함수란?
signal()함수와 같이 시그널 속성을 설정하는 함수이다.
sigaction()은 시그널 핸들러를 만들 때, signal()보다는 복잡하지만 이식성이 좋은 장점이 있다.
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
성공하면 '0', 에러가 발생하면 '-1'을 리턴한다.
2. sigaction 구조체
sigaction()함수의 두 번째 매개변수로 정의는 아래와 같다.
struct sigaction {
void (*sa_handler) (int); 시그널 핸들러 void (*sa_sigaction) (int, siginfo_t *, void *); 시그널 핸들러로 위와 같다 sigset_t sa_mask; 시그널 핸들러가 동작 중 블록되는 시그널집합 int sa_flags; void (*sa_restorer) (void); 응용프로그램에서 사용용도가 아니다. }
여기서 sa_flag에 대해서 자세히 알아본다. sa_flag는 시그널이 어떻게 처리돼야 하는지를 제어한다.
여러 옵션은 아래와 같으며 OR 연산으로 같이 쓸 수 있다.
- sig가 SIGCHLD인 경우, 시그널을 추출한 결과로 자식프로세스가 멈추거나 재개될 때,
이 시그널을 생성하지 않는다.
- sig가 SIGCHLD인 경우에, 자식들이 제거될 때 좀비로 변하지 않는다.
- 이 시그널이 잡히면, 핸들러가 실행 중일때 프로세스 시그널 마스크에 자동적으로 추가하지 않는다.
- sigaltstack()에 설치된 대체 스택을 사용해 이 시그널에 대한 핸들러를 실행한다.
- 이 시그널이 잡히면, 핸들러를 실행하기 전에 기본 속성(SIG_DLF)으로 변경한다.
- 시그널 핸들러에 의해 중지된 시스템 호출을 자동적으로 재시작한다.
- 시그널에 대한 더 많은 정보를 제공하는 추가적인 인자로 시그널 핸들러를 실행한다.
3. 예제
/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#define MAX_SLEEP_TIME 2
static int sigCnt[NSIG];
static volatile sig_atomic_t gotSigint = 0;
void printSigSet(FILE *of, const char *prefix, const sigset_t *sigset)
{
int sig, cnt;
cnt = 0;
for(sig = 1; sig < NSIG /*32*/; sig++)
{
if(sigismember(sigset, sig))
{
cnt++;
fprintf(of, "%s%d (%s)\n", prefix, sig, strsignal(sig));
}
}
if(cnt == 0)
fprintf(of, "%s<empty signal set >\n", prefix);
}
void signal_handler(int sig)
{
sigset_t pendingMask;
if(sig == SIGINT)
gotSigint = 1;
else
{
sigCnt[sig]++;
if(sigpending(&pendingMask) == -1)
fprintf(stderr, "sigpending\n");
printSigSet(stdout, "\t\t", &pendingMask);
sleep(MAX_SLEEP_TIME);
}
}
int main(int argc, char *argv[])
{
int i;
struct sigaction sigact;
//sigset_t pendingMask;
printf("%s: PID is %ld\n", argv[0], (long)getpid());
sigact.sa_handler = signal_handler;
sigfillset(&sigact.sa_mask);
//sigact.sa_flags = SA_RESETHAND | SA_RESTART;
sigact.sa_flags = SA_RESTART;
sigaction(SIGUSR2, &sigact, NULL);
sigaction(SIGINT, &sigact, NULL);
while(!gotSigint)
continue;
for(i = 1; i < NSIG; i++)
{
if(sigCnt[i] != 0)
printf("%s: signal %d caught %d time%s \n", argv[0], i, sigCnt[i], (sigCnt[i] == 1) ? "" : "s");
}
exit(EXIT_SUCCESS);
return 0;
}
4. 설명
앞 장에서 시그널을 전송하고 수신하는 예제에서 수신하는 프로그램을 수정하였다.
기존 앞 장은 signal()함수를 사용하였으나 여기서는 sigaction()함수로 대체하였다.
기존 앞 장에서는 signal()함수를 쓸 때는 여러가지 추가적인 시그널 처리 함수들을 호출하였다.
하지만 sigaction()함수는 그런 추가적인 함수 호출없이 동일한 기능을 할 수 있다.
System Program/signal 2013. 7. 8. 12:00
1. 시그널 전송 프로그램
시그널 SIGUSER2에 대해서 kill()시스템 함수를 통하여 1000000번을 설정한 PID 프로세스에 전송한다.
/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#define MAX_SEND_SIGNUM 1000000
int main(int argc, char *argv[])
{
int i;
pid_t pid;
puts("insert PID");
scanf("%ld", &pid);
getchar(); // fflush(stdin);
printf("%s: sending signal %d to process %ld %d times\n", argv[0], SIGUSR2, (long)pid, MAX_SEND_SIGNUM);
for(i = 0; i < MAX_SEND_SIGNUM; i++)
if(kill(pid, SIGUSR2) == -1)
fprintf(stderr, "kill error [%s]\n", strerror(errno));
printf("%s: exiting\n", argv[0]);
exit(EXIT_SUCCESS);
return 0;
}
2. 시그널 수신 프로그램
위의 송신 프로그램에서 보낸 시그널을 수신하여 시그널별로 얼마나 처리가 되었는지 결과를 카운팅한다.
/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#define MAX_SLEEP_TIME 25
static int sigCnt[NSIG];
static volatile sig_atomic_t gotSigint = 0;
void printSigSet(FILE *of, const char *prefix, const sigset_t *sigset)
{
int sig, cnt;
cnt = 0;
for(sig = 1; sig < NSIG /*32*/; sig++)
{
if(sigismember(sigset, sig))
{
cnt++;
fprintf(of, "%s%d (%s)\n", prefix, sig, strsignal(sig));
}
}
if(cnt == 0)
fprintf(of, "%s<empty signal set >\n", prefix);
}
void signal_handler(int sig)
{
if(sig == SIGINT)
gotSigint = 1;
else
sigCnt[sig]++;
}
int main(int argc, char *argv[])
{
int n;
sigset_t pendingMask, blockingMask, emptyMask;
printf("%s: PID is %ld\n", argv[0], (long)getpid());
for(n = 1; n < NSIG; n++)
(void) signal(n, signal_handler);
sigfillset(&blockingMask);
if(sigprocmask(SIG_SETMASK, &blockingMask, NULL) == -1)
fprintf(stderr, "sigprocmask\n");
printf("%s : sleeping for %d seconds\n", argv[0], MAX_SLEEP_TIME);
sleep(MAX_SLEEP_TIME);
if(sigpending(&pendingMask) == -1)
fprintf(stderr, "sigpending\n");
printf("%s : pending signals ars : \n", argv[0]);
printSigSet(stdout, "\t\t", &pendingMask);
sigemptyset(&emptyMask);
if(sigprocmask(SIG_SETMASK, &emptyMask, NULL) == -1)
fprintf(stderr, "sigprocmask\n");
while(!gotSigint)
continue;
for(n = 1; n < NSIG; n++)
if(sigCnt[n] != 0)
printf("%s: signal %d caught %d time%s \n", argv[0], n, sigCnt[n], (sigCnt[n] == 1) ? "" : "s");
exit(EXIT_SUCCESS);
return 0;
}
3. 설명
수신 프로그램은 초기 시그널 마스크를 sigfillset()함수를 통해서 모두 블록으로 초기화하고 25초간 수면상태로 진입 후
다시 모든 시그널을 블록하지 않는 구조이다.
송신 프로그램에서 보낸 시그널이 얼마나 처리되느냐는 위의 각 구간에서 얼마나 시그널이 수신되었느냐에 따라 다르다.
만약 초기 블록 후 수면상태까지 시그널이 모두 전송되었다면 단 하나의 시그널만 처리될 것이다. 왜냐하면 초기 블록된 상태에서 수신된 동일 시그널은 모두 보류 상태로 있고 보류 상태가 끝나면 단 하나의 시그널만 처리되기 때문이다.
하지만 만약 수면 상태 후에 시그널이 모두 '0'으로 초기화된 후에 전송된 시그널은 커널의 context switching을 고려하여
많은 수가 처리될 것이다.
4. 결론
시그널 방식은 비동기적인 방식으로 시그널이 프로세스에 제대로 전달되었는지 확인할 수 있는 방법이 없다.
또한 경우에 따라서는 처리가 되지 않고 버려지기 때문에 신호가 전달되었는지를 신뢰할 수 없으므로 비신뢰적인 특징을 가진다고 말할 수 있다.
System Program/signal 2013. 7. 8. 11:57
1. Kill 함수를 이용하여 시그널 보내기
signal을 전송하기 위한 가장 간단한 방법은 kill(2) 시스템함수를 이용하는 것으로, kill의 사용방법은 다음과 같다. - shell의 kill 명령과 혼동하지 말자. -
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig) ;
성공하면, '0'을 리턴, 에러가 발생하면 '-1'을 리턴
pid는 시그널을 받을 프로세스의 pid로 그룹 혹은 특정 pid에 시그널을 보낼 수 있다.
- pid > 0 : pid에 대응되는 프로세스에 시그널을 보낸다.
- pid == 0 : 현재프로세스에 속한 모든 그룹의 프로세스에 시그널을 보낸다.
- pid == -1 : 1번 프로세스 (init) 를 제외한 모든 프로세스에 시그널을 보낸다.
- pid < -1 : 프로세스 그룹에서 절대값으로 동일한 모든 프로세스에게 시그널을 보낸다.
어떤 프로세스도 명시된 pid와 일치하지 않으면 kill()은 실패하고 errno를 ESRCH(해당프로세스없음)로 설정한다.
프로세스가 요청된 PID에 시그널을 보낼 권한이 없는 경우 kill()은 실패하고, errno는 EPERM으로 설정한다.
2. Raise() 시그널 전송
프로세스 자기 자신에게 시그널을 보낼 때 사용된다.
예제는 생략한다.
#include <sys/types.h>
#include <signal.h>
int raise(int sig) ;
성공하면, '0'을 리턴, 에러가 발생하면 '0'이 아닌 값을 리턴
3. killpg()를 통한 시그널 전송
프로세스 그룹의 모든 맴버에게 시그널을 전송한다.
#include <sys/types.h>
#include <signal.h>
int killpg(pid_t pgrp, int sig) ;
성공하면, '0'을 리턴, 에러가 발생하면 '-1'값을 리턴
System Program/signal 2013. 7. 8. 11:26
1. 시그널의 종류
앞장에서 시그널은 표준과 전통적인 두 가지의 범주로 나눌 수 있다고 했다.
아래 표는 일반적인 시그널의 리스트이다.
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX
2. 시그널 확인 방법
쉘 상에서 아래와 같은 명령어로 표준 시그널 정보를 확인할 수 있다.
$ kill -l
3. kill 명령어 사용법
$ kill -[시그널번호] [pid]
[예]
$ kill -SIGKILL 100 ( kill -9 100)
4. 자주 쓰는 시그널 정리
CTRL + C : SIGINT (터미널 Interrupt)
CTRL + Z : SIGSTP (프로세스 중단)
CTRL + \ : SIGQUIT (Core Dump를 남기고 프로세스 종료)
5. 시그널의 범주
시그널은 제어가능한 것과 그렇지 않은 것으로 나눈다. 제어가능 한 것은 시그널 Catch가 가능한 것이고 반대는 그렇지 않은 것이다.
일반적으로 SIGKILL, SIGSTOP은 제어가 불가능하다.
6. reference
http://man7.org/linux/man-pages/man7/signal.7.html
|