[시스템프로그래밍] Signals

2023. 12. 3. 15:37·Study/시스템프로그래밍

signal은 이벤트가 일어났을 때 발생했다는 사실을 프로세스에게 전달해주는 software notification다.

 

명령어 kill을 주면 목적지로 시그널을 준다.

  • -l option은 available symbolic signal names의 list들을 준다.

프로세스는 도착한 시그널에 대해서 처리를 해줘야한다.

→ 시그널이 왔을 때 프로세스가 취해줘야 되는 default action이 있다.

터미널에서 ctrl+c → interrupt signal이 발생해서 지금 돌고 있는 프로세스에게 전달이 된다. 그러면 프로세스는 하던 작업을 멈추고 default action을 수행한다.(여기서는 강제 종료)

 

Generating Signals

 

#include <signal.h>

int kill(pid_t pid, int sig);

 

마지막 형태의 kill은 signal 0에 대해 0, SIGHUP에 대해 1, SIGINT에 대해 2, SIGQUIT에 대해 3, SIGABRT에 대해 6, SIGKILL에 대해 9, SIGALRM에 대해 14, SIGTERM에 대해 15의 signal_number 값을 지원합니다.

예시: kill -9 3423

 

pid parameter

  • target process ID
  • 0
  • -1

return value

  • 0 → successful, -1 → unsucessful

 

시그널을 받았을 때 무엇을 해야하는가?

프로세스는 시그널이 언제 도착할지 모른다.

  • 시그널이 언제 도착하든 프로세스가 하던 작업에 방해가 되지 않고 시그널을 받을 수 있게 대비를 해야한다.

 

시그널이 오는 것을 막고 싶다 : signal mask

벽 안에 막고 싶은 signal들을 집어넣을 수 있다.

나에게 오는 시그널을 도착하도록 허용하든가, 못 오게 막을 수 있다.

 

sigfrommask 함수를 활용

sigaction 함수를 통해서 시그널이 왔을 때 어떻게 해야 할지 정할 수 있음

signal을 파라미터로 넘길 때 여러 signal을 설정해야 될 때 signal sets를 활용한다.

 

#include <signal.h> int sigaddset(sigset_t* set, int signo);

→ sigset 안에 signal들을 넣을 수 있다.

 

int sigdelset(sigset_t* set, int signo);

→ sigset 안에 signal을 뺄 수 있다.

 

int sigemptyset(sigset_t* set);

→ sigset 초기화

 

int sigfillset(sigset_t* set); int sigismember(const sigset_t* set, int signo);

 

Signal masks

#include <signal.h>
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);

 

함수 sigprocmask로 시그널을 넣을 수도 뺄 수도 있다.

  • 첫 번재 파라미터 how로 판단할 수 있다.
  • SIG_BLOCK : signal mask에 두번째 파라미터에 있는 sigset을 추가하자.
    • 만약에 같은 숫자의 시그널이 프로세스에 접근하면 막힌다.(막힌 시그널 → pending signal, pending signal list에 들어가서 대기하게 된다.)
  • SIG_UNBLOCK: signal mask에 두번째 파라미터에 있는 sigset을 빼자.
  • SIG_SETMASK: signal mask에 있는 signal들을 전부 빼고 두번째 파라미터에 있는 signal들을 집어넣자.(교체)
  • 마지막 파라미터 oset의 용도: output 파라미터, 시그널 마스크가 변경되기 전 상태를 세번째 oset에 담아서 output으로 전달한다.)
  • 다시 원래대로 돌릴려면 sigprocmask를 다시 실행해서 oset을 두번째 파라미터로 주면 된다.

 

Catching and ignoring signals

시그널이 왔을 때 default action이 아닌 다른 action을 하고싶다면?

#include <signal.h>
int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);

→ 시그널이 왔을 때 무슨 action을 취할지 등록하는 함수(시그널 핸들러 함수)

 

세번째 파라미터 oact는 oset과 같이 원래 상태를 담는다.

시그널 핸들러 함수는 아무 이름으로 써는데 파라미터와 리턴 타입은 정의가 되있다.

파라미터: int

리턴 타입: void

 

 

그 외에도 다른 옵션들을 정의할 수 있게 구조체가 제공이 된다.

struct sigaction{
void (*sa_handler)(int); /* SIG_DFL, SIG_IGN or pointer to function */
sigset_t sa_mask; /* additional signals to be blocked during execution of handler*/
int sa_flags; /* special flags and options */
void (*sa_sigaction) (int, siginfo_t *, void *); /* realtime handler */
}

 

void (*sa_handler)(int) → 시그널 핸들러 등록하는 자리

 

**SIG_DFL**은 해당 시그널에 대해 기본 동작을 수행하도록 지정한다.

**SIG_IGN**은 해당 시그널을 무시하도록 지정한다.

 

프로세스는 메인 함수에 어느 부분을 수행하다가 시그널이 올지 알 수 없다.

→ 시행하던 작업을 멈추고 signal handler 함수를 부른다. 보통의 경우에는 문제가 잘 안생기지만, 문제가 생기는 경우가 있다.

 

예시)

doneflag에 접근하는 코드는 critical section(임계 영역)이다. 왜냐하면 시그널 핸들러가 해당 값을 수정할 수 있고, 메인 함수에서도 그 값을 조사하기 때문이다.

→ 다중 스레드가 하나의 변수를 동시에 접근할 때 발생한 문제가 나타날 수 있다.

메인 함수에서 건드리고 있었던 변수를 시그널 핸들러에서 건드리지 못하게 해야한다.

  • doneflag는 int인데, doneflag를 sig_atomic_t로 선언한다. 그러면 커널 레벨에서 원자적인 operation을 보장한다.(이 operation을 더 이상 쪼갤 수 없다. → 방해받지 않고 끝까지 수행할 수 있다.)

(volatile 한정자는 컴파일러에게 해당 변수가 프로그램 실행과 비동기적으로 변경될 수 있음을 알려준다.)

다른 예시)

 

이번에는 int가 아니라 배열이다. → sig_atomic_t 사용 불가능.

 

메인 함수에서도, 시그널 핸들러에서도 배열에 접근한다.

→ signal mask를 사용해서 시그널이 접근하지 못하게 제어하면 된다.

 

Waiting for signals

사전에 필요한 일을 다했고, 이제 시그널만 받으면 될 때, 시그널을 기다리는 기능은 어떻게 구현해야 할까?

시그널을 기다리는 함수들:

 

pause(), sigsuspend(), sigwait()

(pause()는 문제가 발생할 여지가 있음)

#include <unistd.h>
int pause(void);

 

pause()는 아무 시그널이나 와도 return이 된다. 그래서 while문으로 감싸줘서 내가 기다리는 시그널인지 판별해준다.

static volatile sig_atomic_t sigreceived = 0;

while( sigreceived == 0 )
	pause();

 

sigsuspend()

#include <signal.h>
int sigsuspend(const sigset_t* sigmask);

시그널 마스크를 sigmask가 가리키는 마스크로 설정하고, 프로세스가 시그널을 잡을 때까지 프로세스를 중단시킨다. sigmask 매개변수는 프로그램이 찾고 있는 시그널을 언블록하는 데 사용될 수 있다. 반환되면 시그널 마스크는 sigsuspend가 호출되기 전의 값으로 재설정된다.

 

sigwait()

#include <signal.h>
int sigwait(const sigset_t *restrict sigmask, int *restrict signo);
  1. set: 기다릴 시그널 집합을 나타내는 sigset_t 구조체의 포인터
  2. sig: 발생한 시그널 번호가 저장될 포인터

작동 방식:

  • sigwait 함수는 전달된 시그널 집합 **set**에 포함된 시그널 중 하나가 발생할 때까지 블록된다.
  • 시그널이 발생하면 해당 시그널 번호가 **sig**에 저장되고, 함수는 반환된다.
  • 시그널은 발생해도 디폴트 동작이 수행되지 않고 블록되어 있는 **sigwait**에서 처리된다.

 

suspend()와의 차이점

  • sigmask 매개변수는 기다릴 시그널 집합을 유지하므로 집합에 있는 시그널만이 sigwait를 반환할 수 있다.
  • 프로세스 시그널 마스크를 변경하지 않는다.
  • sigwait를 호출하기 전에 sigmask에 있는 시그널은 차단되어 있어야 한다.
  1. 사용 방식:
    • sigsuspend: 현재의 시그널 마스크를 변경하고 특정 시그널이 발생할 때까지 대기. 대기하는 동안에는 시그널이 블록되어 처리되지 않습니다. 대기가 끝나면 이전의 시그널 마스크로 복원된다.
    • sigwait: 지정된 시그널 집합에 포함된 시그널 중 하나가 발생할 때까지 대기한다. 시그널이 발생하면 해당 시그널 번호를 반환하고, 시그널은 블록되지 않고 디폴트 동작이 수행되지 않는다.
  2. 인자 및 반환값:
    • sigsuspend: 시그널 마스크를 변경하므로 **sigsuspend**는 따로 시그널 집합을 명시적으로 지정하지 않는다. 반환값은 항상 -1이며, 시그널이 처리되면 중단되는 경우가 많다.
    • sigwait: 기다리는 동안에는 시그널이 블록되지 않기 때문에, **sigwait**은 시그널 집합을 명시적으로 지정해야 합니다. 반환값은 시그널이 발생할 때 해당 시그널의 번호가 된다.
  3. 대기 동작:
    • sigsuspend: 특정 시그널이 발생할 때까지 대기하고, 대기 중에 다른 시그널은 블록되어 처리되지 않는다.
    • sigwait: 지정된 시그널 집합에 포함된 시그널 중 하나가 발생할 때까지 대기하고, 다른 시그널은 블록되지 않는다.
  4. 활용:
    • sigsuspend: 시그널 마스크를 임시로 변경하여 특정 시그널을 처리하고자 할 때 사용된다.
    • sigwait: 특정 스레드에서 특정 시그널을 처리하도록 지정할 때, 멀티스레드 환경에서 편리하게 사용된다.

Async-signal safe 함수: signal에 안전한 함수(많지 않다)

 

printf도 Async-signal safe 함수가 아니다.

저작자표시 비영리 변경금지 (새창열림)

'Study > 시스템프로그래밍' 카테고리의 다른 글

[시스템프로그래밍] 스레드  (0) 2023.12.04
[시스템프로그래밍] 시간과 타이머  (0) 2023.12.03
[시스템프로그래밍] pipe에 관하여  (1) 2023.11.02
[시스템프로그래밍] 디렉토리 엔트리, hard/symbolic link, stat에 관하여  (0) 2023.11.02
[시스템프로그래밍] 프로그램, 프로세스, 스레드에 관하여  (2) 2023.10.26
'Study/시스템프로그래밍' 카테고리의 다른 글
  • [시스템프로그래밍] 스레드
  • [시스템프로그래밍] 시간과 타이머
  • [시스템프로그래밍] pipe에 관하여
  • [시스템프로그래밍] 디렉토리 엔트리, hard/symbolic link, stat에 관하여
퀵차분
퀵차분
웹 프론트엔드 개발자를 꿈꾸고 있습니다 :)
  • 퀵차분
    QC's Devlog
    퀵차분
  • 전체
    오늘
    어제
    • 분류 전체보기 (165) N
      • Frontend (28)
        • HTML, CSS (7)
        • Javascript (3)
        • React (11)
        • Typescript (2)
        • Next.js (4)
      • Node.js (3) N
      • Study (40)
        • Modern JS Deep Dive (13)
        • SQL (1)
        • Network (1)
        • 프롬프트 엔지니어링 (4)
        • 인공지능 (9)
        • 시스템프로그래밍 (11)
        • 선형대수학 (1)
      • Intern (4)
      • KUIT (20)
      • Algorithm (48)
        • Baekjoon(C++) (26)
        • Programmers(JavaScript) (22)
      • 우아한테크코스(프리코스) (4)
      • Project (7)
        • PROlog (4)
        • Nomadcoder (2)
      • 생각 (4)
      • Event (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    백준
    HTML
    알고리즘
    음악추천
    시스템프로그래밍
    typescript
    javascript
    인공지능
    자바스크립트
    next.js
    KUIT
    프로그래머스
    프롬프트 엔지니어링
    프론트엔드
    타입스크립트
    오블완
    리액트
    티스토리챌린지
    프로그래머스 자바스크립트
    react
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
퀵차분
[시스템프로그래밍] Signals
상단으로

티스토리툴바