시그널
- software interrupts
- 비동기적으로 발생하는 이벤트를 처리하기 위한 매커니즘을 제공
Interrupt
Interrupt Handling
- 인터럽트 발생
- 프로세스 중단(커널에 의해서)
- 인터럽트 처리
- 인터럽트 발생 장소, 원인 파악
- 인터럽트 서비스 할 것인지 결정
- 인터럽트 서비스 루틴 호출
Life cycle of a signal
- 발생
- 프로그램에서 발생한 예외적 상황 ex) divide by zero
- 사용자의 입력 ex) ctrl+c
- process 또는 kernel에서 생성/전달
- 보관
- signal 전달 전까지, kernel이 보관
- 전달 가능해지면, 해당 process에게 전달
- 처리
- 지정된 방법에 따라 signal 처리
- ignore
- catch
- default action
Signals in Unix/Linux
- Symbolic constant(map to a number)
- Pending signal : 아직 처리되지 않은 signal
Signal handler
- 특정 signal을 처리하기 위해 지정된 함수
- default handler를 대체 할 수 있음
- 단, 다음 signal에 대한 handler는 대체 불가능
registering a signal handler
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
- signum : 처리할 signal 번호
- handler : signal handler의 function pointer
- SIG_IGN : signal 무시
- SIG_DFL : system의 default handler로 처리
- return
- 기존 handler의 function pointer
- SIG_ERR : error
print signal message
#include <signal.h>
void psignal(int sig, const char* msg);
char* strsignal(int sig);
extern const char* const sys_siglist[];
Example
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigint_handler(int signo)
{
printf("Caught SIGINT!\n");
psignal(signo, "Received signal");
}
int main(void) {
if (signal(SIGINT, sigint_handler) == SIG_ERR) {
fprintf(stderr, "Cannot handle SIGINT!\n");
exit(EXIT_FAILURE);
}
for (;;) pause();
return 0;
}
Inherited signal behavior
- on fork() : signal handler 상속 받음, pending signal 상속 받지 않음
- on exec() : signal handler 상속 받지 않음, pending signal 상속
sending a signal
#include <sys/types.h>
#include <signal.h>
int kill (pid_t pid, int signo);
- pid : signal을 보낼 대상
- 0 : 자신이 속한 process group 내 모든 process
- -1 : 현재 process가 가진 권한으로 signal을 보낼 수 있는 모든 process
- < -1 : GID == | pid | 인 process group
- signo : 보낼 시그널 번호
- return
- 0 : success
- -1 : fail
- errno = EINVAL : 유효하지 않은 signo
- errno = EPERM : signal을 보낼 권한이 없음
- errno = ESRCH : 대상 process가 존재하지 않음(or zombie)
Permission for sending a signal
- 다른 프로세스에게 signal을 보내기 위해서는 적합한 권한을 가지고 있어야 함
- 필요 권한
- Sender's (RUID or EUID) == (RUID or SUID) of the receiver
- root는 모든 process에게 signal 전달 가능
- 권한 체크 방법
- NULL signal (=0) 활용
- 실제 시그널을 전달하지 않으나, error checking은 수행
int ret;
ret = kill(1722, 0);
if (ret); // lack of permission
else; // we have permission
Example
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
extern int errno;
int main(void) {
int targetPID = 0;
while (1) {
printf("Enter PID to kill : ");
scanf("%d", &targetPID);
if (kill(targetPID, SIGKILL) == -1) {
switch (errno) {
case EPERM:
printf("Not enough permission!\n");
break;
case ESRCH:
printf("Cannot find the process %d\n", targetPID);
break;
}
}
else {
printf("Bang! -> %d\n", targetPID);
}
}
return 0;
}
sending a signal to yourself
#include <signal.h>
int raise(int sig);
- 자신에게 signal 전달
- sig : 보낼 signal 번호
- return
- 0 : success
- otherwise : fail
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
Example
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void handler(int signo) {
psignal(signo, "Received Signal");
}
int main(void) {
signal(SIGALRM, handler);
alarm(2);
printf("Wait...\n");
sleep(3);
return 0;
}
Blocking signals
- signal은 임의의 순간에 발생
- kernel은 임의의 순간에 process를 중지
- signal handling 작업 수행
- 중요한 작업 수행 중 signal이 온다면?
- critical region 보호를 위해 signal blocking 필요
signal set
- 복수 개의 signal을 처리하기 위해 사용
- sigset_t(=bit mask) : 각 bit가 signal number와 1:1 mapping
#include <signal.h>
int sigemptyset(sigset_t* set);
int sigfillset(sigset_t* set);
int sigaddset(sigset_t* set, int signum);
int sigdelset(sigset_t* set, int signum);
int sigismember(const sigset_t* set, int signum);
blocking signals
#include <signal.h>
int sigprocmask(int how, const sigset_t* set, sigset_t* oldset);
- how : 지정 방법
- SIG_SETMASK : set을 blocking mask로 적용
- SIG_BLOCK : blocking 대상 추가
- SIG_UNBLOCK : blocking 대상에서 제거
- set : 적용할 signal set
- NULL : how를 무시(signal mask 유지), 현재 signal mask -> oldset
- oldset : 적용 전 signal set 반환
- return
- 막힌 signal은 pending되고, pending signal들은 blocking이 풀리면 전달됨
Example
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void handler_SIGINT(int _signo)
{
printf("Received signal : %s\n", strsignal(_signo));
}
int main(void) {
sigset_t new;
if (signal(SIGINT, handler_SIGINT) == SIG_ERR) {
perror("signal SIGINT");
exit(1);
}
sigemptyset(&new);
sigaddset(&new, SIGINT);
sigaddset(&new, SIGQUIT);
sigprocmask(SIG_BLOCK, &new, (sigset_t*)NULL);
sleep(5); //critical section
printf("UnBlocking signals\n");
sigprocmask(SIG_UNBLOCK, &new, (sigset_t*)NULL);
return 0;
}
waiting signals
#include <signal.h>
int sigsuspend(const sigset_t* mask);
- signal mask를 임시로 교체 후, block 되지 않은 signal이 도착할 때까지 대기
- mask : 교체할 signal set의 주소
- return
- 항상 -1
- errno = EINTR : signal에 의해 interrupt 발생
Example
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void handler(int signo) {
psignal(signo, "Received Signal:");
}
int main(void){
sigset_t set;
signal(SIGALRM, handler);
sigfillset(&set);
sigdelset(&set, SIGALRM);
alarm(3);
printf("Wait...\n");
sigsuspend(&set);
return 0;
}