Signal

EEEFFEE·2023년 8월 5일

System Programming

목록 보기
11/19

23.08.05 최초 작성
23.11.14 sigaction, 타이머 추가

  • 비동기적 이벤트를 핸들링하기 위한 메커니즘을 제어하기 위한 Software Interrupt

  • 신호처리의 순서

    1. 발생 (Raise) : 프로그램에서 발생한 예외적인 상황에서 발생. 프로세스나 kernel에서 생성 or 전달.
    2. 보관 (Store) : 신호 전달 전 까지 kernel이 보관. 전달 가능해지면 해당 프로세스에게 전달.
    3. 처리 (Handling) : 지정된 방법에 따라 신호 처리. 수행하던 프로세스를 중단하고 지정한 방식(Signal Handler)으로 작업을 수행.
  • Pending Signal : 아직 처리되지 않은 신호

  • Linux에서 신호의 종류

1. 신호 처리

Signal Handler : 특정 신호를 처리하기 위해 지정된 함수. SIGKILL, SIGSTOP을 제외하고 수정 가능함.


#include <signal.h>
typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

///
signum : 처리할 신호 번호
handler : Signal Handler의 함수 포인터 (SIG_IGN : 신호 무시, SIG_DFL : default handler로 처리)

Return : 기존 handler의 함수 포인터
(SIG_ERR : Error)

1.1 신호 메시지 출력


#include <signal.h>

void psignal(int sig, const char *msg);

///
sig : 신호 id
msg : 설명 앞에 출력할 메시지

#include <string.h>

char* strsignal(int sig);

///
sig : 신호

Return : sig에 대한 신호 설명을 문자열로 반환

#include <signal.h>

extern const char *const sys_siglist[];

///
전역 변수를 extern으로 가져와서 사용 가능

1.2 sigaction

  • sigaction : 특정 신호에 관련된 동작을 조회/수정할 수 있는 함수

#include <signal.h>

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

///
signum : 시그널 번호를 의미합니다. (SIGKILL과 SIGSTOP 불가)

act : signum에 대해 동작할 함수
oldact : 시그널에 대한 동작을 조회하는 정보입니다.

struct sigaction {
   void     (*sa_handler)(int);
   void     (*sa_sigaction)(int, siginfo_t *, void *);
   sigset_t   sa_mask;
   int        sa_flags;
   void     (*sa_restorer)(void);
};

1.3 신호 상속 관계

  1. fork()
  • Signal 처리 방식 상속 받음
  • Pending Signal은 상속 받지 못함
  1. exec()
  • Signal Handler 상속 받지 않음
  • Pending Signal 상속 받음

2. 신호 전송


#include <sys/types.h>
#include <signal.h>

int kill(pit_t pid, int signo);

///
pid : 신호를 보낼 대상(0 : 자신이 속한 Process Group 내 모든 프로세스, -1 : 현재 프로세스가 가진 권한으로 신호를 보낼 수 있는 모든 프로세스, < -1 : GID == (-pid)인 Process Group)
signo : 보낼 신호 번호

Return
(0 : Success, -1 : Fail)

#include <signal.h>

int raise(int sig); //자신에게 신호 전송

///
sig : 전송할 신호

Return 
(0 : Success, Otherwise : Fail)

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

///
seconds : 시간 지정()

Return : SIGALRM 발생

2.1 신호 전송 권한

  • 다른 프로세스에게 신호를 보내기 위해 권한 필요
  • 필요 권한
    • 전송자의 RUID or EUID = 수신자의 RUID or EUID
    • Root는 모든 프로세스에게 전달 가능
    • Null signal(=0)을 활용해 권한 확인 가능

3. 신호 차단

  • Critical Region 보호를 위해 신호를 차단

3.1 Signal Set

  • 복수의 신호를 처리하기 위해 사용
  • sigset_t : 각 bit가 신호 번호와 1대1 대응 됨

#include <signal.h>

int sigemptyset(sigset_t *set); //set 비우기
int sigfillset(sigset_t *set); // set을 다 채우기

int sigaddset(sigset_t *set, int signum); // 신호 set에 추가
int sigdelset(sigset_t *set, int signum); // 신호 set에서 삭제

int sigismember(const sigset_t *set, int signum); // Return 1 : set에 해당 신호 존재

#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 유지)
oldset : 적용 전 signal set 저장

Return 
(0 : Sucess, -1 : Fail)

Pending Signal들은 blocking이 풀리면 전달 됨

4. 신호 대기


#include <signal.h>

int sigsuspend(const sigset_t *mask); // Signal Mask를 임시 교체 후 block되지 않은 signal이 도착할 때 까지 대기

Return 
(항상 -1, errno = EINTR : 신호에 의해 interrupt 발생)

5. 타이머

  • 일정 시간마다 시그널을 발생시키는 기능
  • 현재 작업을 중단하고 타이머 시그널 핸들러 호출
  • setitimer : 설정한 시간마다 event 발생하도록 설정
    타이머 만료를 전달받는 방법이 시그널이며 만료 시그널이 다양한 타이머에서 발생할 시 몇몇 타이머는 무시된다 (시그널은 하나만 받아들일 수 있음)

#include <sys/time.h>

int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

///
which : timer type
value : 설정할 시간 간격 값
oval : 현재의 시간 간격 값

Return
(0 : Sucess, -1 : Fail)

whichITMER_REALITIMER_VIRTUREITIMER_PROF
의미실제 시간 기준user mode 시간 기준user / kernel mode 시간 기준
  • getitimer : 현재 설정된 시간 간격 확인

#include <sys/time.h>

int getitimer(int which, struct itimerval *curr_value);

Return
(0 : Sucess, -1 : Fail)
  • sleep : 일정 시간동안 작업 중지

#include <unistd.h>

unsigned int sleep(unsigned int seconds);

Return
(0 : Sucess, unsinged int : 일찍 sleep이 끝났을 때 sleep하지 않은 시간 표시)

5.1 POSIX TIMER

  • setitimer의 단점을 보완하고자 사용하는 방식
  • 주로 RTOS같은 실시간 사용을 보장해야하는 상황에서 사용
  • timer_create : 타이머 생성

#define _POSIX_C_SOURcE_199309
#include <signal.h>
#include <time.h>

int timer_create(clockid_t clockid, sturct sigevent *evp, timer_t *timerid);

///
clockid : 어떤 타이머를 사용할지 입력
evp : 타이머가 만료될 때 호출자가 알림받는 방법을 나타냄
timeid : 생성된 timeid 저장하는 공간

Return
(0 : Sucess, -1 : error)

clockidCLOCK_REALTIMECLOCK_VIRTUALCLOCK_PROF
의미실제 시간 기준user mode 시간 기준user / kernel mode 시간 기준
  • nanosleep : 일정 시간동안 작업 중지 시키는 함수로 sleep보다 고해상도의 기능을 지원함

#define _POSIX_C_SOURCE 199309
#include <time.h>

int nanosleep(const struct timespec *request, struct timespec *remain);

request.tv_sec = 1;
request.tv_nsec = 123;		//멈추는 시간 1.123초

///
request : 멈추고 싶은 시간을 저장하는 구조체
remain : request에 저장된 시간 - 실제 멈췄던 시간 저장하는 공간

Return
(0 : Sucess, -1 : error / sleep이 중단되었을 때)

timespec 구조체tv_sectv_nsec
의미멈추고 싶은 시간의 (sec)멈추고 싶은 시간의 소숫점 아래 숫자

  • timer_settime, timer_gettime : POSIX 버전 setitimer, getitimer

#include <time.h>

int timer_settime(timer_t timerid, int flags,
                  const struct itimerspec *restrict new_value,
                  struct itimerspec *restrict old_value);
int timer_gettime(timer_t timerid, struct itimerspec *curr_value);

///
timerid : 조작하고자 하는 timer를 나타내는 timer_t 인자
flags : 타이머 설정
new_value : 설정하고자 하는 시간 간격
old_value : 이전에 설정했던 값

curr_value : 확인하고자 하는  간격 값을 저장

Return
(0 : Success, -1 : Fail)

0개의 댓글