<br>
- 수행 결과
<img width="566" alt="image" src="https://user-images.githubusercontent.com/86760744/181876315-302a8771-e183-4134-895c-eb4f02203674.png">
<br><br>
## 시그널 차단
- 프로세스가 코드 어딘가에서 실행 중인 상태에서, 시그널을 수신하게 되면 실행 중인 부분에서 중단되고, 등록된 시그널 핸들러 함수가 수행되게 된다.
- 핸들러 수행이 완료된 후 중단된 지점으로 돌아와 코드를 계속 수행한다.
- 그런데 수행 중인 코드가 수정 중인 데이터를 핸들러에서 참조하거나 수정하게 되면 문제가 발생할 수 있다.
<br>
- 주기적으로 설정 파일에서 읽어들인 데이터를 이용해 어떤 연산을 수행하는 프로세스가 있다고 가정해보자.
- 이 프로세스는 SIGHUP 시그널을 수신 받으면 설정 파일에서 데이터를 다시 읽어오는 핸들러가 등록되어 있다.
- 그런데 10개의 설정 중 3개의 데이터를 읽어와 연산을 수행했는데, 이 시점에 SIGHUP 시그널을 수신 받게 되면,
- 시그널 핸들러가 다시 설정 파일을 읽어오게 되는데, 중단된 시점으로 복귀하게 되면,
- 앞에 읽어온 3개의 시점과, 핸들러 복귀 이후 나머지 7개의 데이터 시점이 달라지게 된다.
- 시그널 핸들러의 복귀 시에, 이와 같이 의도하지 않은 문제가 발생할 수 있다.
<br>
- 이를 위해 10개의 데어티가 다 처리되는 동안은 시그널을 수신 받지 못하게 차단하여 문제를 해결할 수 있다.
- 즉, 프로그램에서 시그널 핸들러와 프로그램의 다른 부분이 데이터를 공유할 필요가 있을 때,
- 공유되는 부분을 크리티컬 섹션이라 하고, 일시적으로 시그널을 보류하여 이 영역을 보호할 수 있다.
<br>
``` c
#include <signal.h>
int sigprocmask (int how, const sigset_t *set, sigset_t *oldset);
how
SIG_SETMASK : 호출한 프로세스의 시그널 마스크를 set으로 변경
SIG_BLOCK : 현재 마스크에 set을 포함
SIG_UNBLOCK : 현재 마스크에서 set을 제거
oldset값이 NULL이 아니면 변경 전의 현재 마스크 값을 oldset에 기록한다.
set값이 NULL인 경우 how를 무시하지만 현재 마스크 값을 획득할 수 있다.
호출에 성공 시 0을 반환하고, 실패 시 -1을 반환 후, errno를 설정한다.
EINVAL : 유효하지 않은 how
EFAULT : 유요하지 않은 set/oldset
#include <signal.h>에 대해
int sigemptyset (sigset_t *set);
시그널 집합 set을 비우며 초기화한다.
int sigfillset (sigset_t *set);
시그널 집합 set에 모든 시그널을 추가하며 초기화한다.
int sigaddset (sigset_t *set, int signo);
시그널 집합 set에 signo 시그널을 추가한다.
int sigdelset (sigset_t *set, int signo);
시그널 집합 set에 signo 시그널을 제거한다.
int sigismemeber (const sigset_t *set, int signo);
시그널 집합 set에 signo 시그널이 포함되어 있으면 1을 반환, 않으면 0을 반환한다.
<br>
- 수행 결과
<img width="377" alt="image" src="https://user-images.githubusercontent.com/86760744/181876379-9c78d575-724e-4a62-b8f5-9b376da145be.png">
<br><br>
## 시그널 고급 관리
- signal() 함수의 대안으로 POSIX는 훨씬 더 강력한 시그널 관리 능력을 제공하는 sigaction() 시스템 콜을 표준화했다.
``` c
#include <signal.h>
int sigaction (int signo, const struct sigaction *act, struct sigaction *oldact);
sigaction()은 act가 NULL이 아닐 경우, signo 시그널에 대한 동작 방식을 지정한다.
sa_flags에 SA_SIGINFO를 설정하면 sa_handler가 아니라 sa_sigaction이 시그널을 처리하는 함수를 명시한다.
void sa_sigaction (int signo, siginfo_t *si, void *ucontext);
#include <sys/signal.h>
typedef struct siginfo_t {
int si_signo; /* 시그널 번호 */
int si_errno; /* errno 값 */
int si_code; /* 시그널을 일으킨 원인을 알려준다.(ex. SI_KERNEL 커널에서 보낸 시그널) */
pid_t si_pid; /* 보내는 프로세스 pid */
uid_t si_uid; /* 보내는 프로세스 uid */
int si_status; /* 종료된 값이나 시그널 */
clock_t si_utime; /* 소비된 사용자 시간 */
clock_t si_stime; /* 소비된 시스템 시간 */
sigval_t si_value; /* si_int와 si_ptr의 유니온 */
int si_int;/* sigqueue() /* 페이로드 정수 타입 */
void *si_ptr; /* sigqueue() 페이로드 void 포인터 타입 */
void *si_addr; /* 장애가 발생한 메모리 위치 */
int si_band;
int si_fd; /* 파일 디스크립터 */
};
si_code 값 등은 sigaction man-page를 참고하면 된다.
sigqueue() 함수를 이용하면 페이로드, 즉 어떤 특정한 데이터를 담아서 시그널을 보낼 수 있다.
수신 받는 프로세스에서 SA_SIGINFO 핸들러로 해당 시그널을 처리하면 보낸 시그널과 데이터를 수신 받을 수 있다.
#inclue <signal.h>
int sigqueue (pid_t pid, int signo, const union sigval value);
union sigval {
int sival_int;
void *sival_ptr;
};
13. 시그널
시그널 개요
시그널(Signal)이란?
# kill -l
명령을 통해 제공하는 전체 시그널 목록을 확인할 수 있다.시그널 전송
시그널 처리
시그널 경쟁
시그널 핸들러
시그널 전송
int main(int argc, char *argv[]) { if (argc < 3) { printf("Usage: %s \n", argv[0]);
return -1;
}
}
how
oldset
값이 NULL이 아니면 변경 전의 현재 마스크 값을 oldset에 기록한다.set
값이 NULL인 경우how
를 무시하지만 현재 마스크 값을 획득할 수 있다.#include <signal.h>
에 대해int sigemptyset (sigset_t *set);
int sigfillset (sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset (sigset_t *set, int signo);
int sigismemeber (const sigset_t *set, int signo);
int wakeup = 0;
static void sig_handler(int signo) { if (signo == SIGINT) printf("signal SIGINT received!\n"); else if (signo == SIGTSTP) printf("signal SIGTSTP received!\n"); else if (signo == SIGQUIT) printf("signal SIGQUIT received!\n"); else printf("signal %d received!\n");
}
static void wait_for_signal(void) { printf("sleep 10 sec\n"); sleep(10); printf("wakeup!\n"); }
int main() { sigset_t set, oldset;
}