Signal?? Sigaction?? 쪽 공부 (feat. chatGPT)

dandb3·2023년 2월 20일
0

이것저것 TMI

목록 보기
1/17

signal에 대하여..?

  • event가 발생했을 경우 (event에 대한 자세한 정의는 잘 모르겠음) kernel은 그 event를 감지하고 signal을 만들어서 process에게 전달해 준다. 이 때 해당 process가 그 signal을 block한 상태였다면 kernel은 queue 안에다가 block된 시그널을 넣는다. 그 후 프로세스가 해당 시그널을 unblock하게 되면 queue 안에 있던 시그널이 차례대로 process에게 전달되게 된다.

오늘의 의문

  • Unblocked signal이 발생한 경우 kernel은 software interrupt를 일으켜서 기존에 진행되던 control을 signal handler로 옮겨준다. 그리고 나서 signal handler가 다 실행되고 난 이후에 다시 control을 기존 signal handler를 부르기 전 상태로 옮긴다.

  • 여기서 생긴 의문: 만약 오래 걸리는 코드 (하나의 명령일 뿐이지만 오래 걸리는 명령) 가 실행되고 있었고, 그 상태에서 signal이 감지되어 handler 함수가 작동하게 되었다면, handler 함수 종료 이후에 control은 기존 실행 흐름으로 돌아올 텐데, 그 위치는 정확히 어디일까?

    1. 실행되고 있었던 코드 이전지점?
    2. 실행되는 부분으로 다시 돌아가서 재개?
    3. 실행되던 코드 바로 다음 코드의 시작점?

정보 탐색

  • 상황에 따라 다르지만 일반적인 경우에는 진행되던 instruction의 바로 다음 instruction 부터 재개된다고 함.

  • process가 system call에 의해 block된 상태라면 (ex. waiting for I/O) signal이 발생했을 때 그 system call은 error code(-1)와 함께 리턴된다. 그 후에 다시 기존 흐름으로 돌아가서 process가 실행된다.

  • 그래서 기존 흐름 이 뭔데요?

    #include <unistd.h>
    #include <stdio.h>
    #include <signal.h>
    #include <string.h>
    
    void	handler(int sig)
    {
        printf("signal handled!\n");
    }
    
    int main(int argc, char **argv, char **envp) {
        char	buf[1024];
        signal(SIGINT, handler);
        read(STDIN_FILENO, buf, 5);
        printf("hoho\n");
        return (0);
    }
    • 요로코롬 코드를 짜서 실행해 보면 결과는 다음과 같다.
    • 그러니까 read 시스템 콜이 input을 기다리는 동안 signal이 발생한다고 하더라도 다시 실행되던 read함수가 재개된다?? 뭔가 이상함.
  • 찾아보니까 sigaction 함수의 sa_flags의 SA_RESTART 값이 있는데, 얘는 시스템 콜 도중에 interrupt가 생기면 시스템 콜을 다시 호출한다고 한다.

  • 즉, 원래 실행되던 시스템 콜 -> signal catch -> signal handler -> return -> 시스템 콜은 error code를 반환 -> 다시 system call 실행 의 단계를 거치고 있었던 것이다.

  • 그냥 signal 함수를 쓰면 시스템 콜은 기본적으로 다시 시작하는 것 같다.

  • 하지만 여기서 sigaction 함수의 강력함이 나타나는데, sa_flags의 SA_RESTART를 0으로 만들어 버리면, 시스템 콜이 error code를 내뱉으며 종료되고 다시 실행되지 않고 다음 instruction으로 넘어가게 된다.

  • 예제 코드

    #include <unistd.h>
    #include <stdio.h>
    #include <signal.h>
    #include <string.h>
    
    void	handler(int sig)
    {
        printf("signal handled!\n");
    }
    
    int main(int argc, char **argv, char **envp) {
        char	buf[1024];
        struct sigaction	sa;
        memset(&sa, 0, sizeof(sa));
        sa.sa_handler = handler;
        sigaddset(&sa.sa_mask, SIGINT);
        sa.sa_flags = 0;
        sigaction(SIGINT, &sa, NULL);
        read(STDIN_FILENO, buf, 5);
        printf("hoho\n");
        return (0);
    }
    • 이렇게 되면 결과는 다음과 같다.
      • read함수가 종료되고 넘어간다!
  • 사실 이 의문이 왜 들었냐하면, bash에서 here_doc 입력을 받는 도중에 SIGINT를 주게 되면, 입력 받던 부분이 모두 종료되고 원래 bash prompt로 돌아가게 되는데, 이전의 입력받던 readline을 signal을 통해 종료시킬 수 있지 않을까? 라는 의문에서 시작되었다.

  • TMI)

    • bash 내부적으로 here_doc의 SIGINT를 어떻게 처리할까?
    • chatGPT에게 물어본 결과, (정확하지 않을 수도..?) here_doc mode 전용으로 만들어진 handler 함수가 있다고 한다.
    • here_doc에서 원래 bash prompt로 돌아갈 때에는 longjmp()함수를 이용하는데, 이는 goto가 local한 영역에서만 jmp가 가능했던 것과는 달리, non-local jmp도 가능하게 한다.
    • 함수 stack-frame이나 지역 변수, 등등이 어떻게 바뀌는지도 물어보았더니 setjmp() 함수를 통해서 현재 메모리, 레지스터 등등의 상태를 jmp_buf 객체에 저장해 놓고, 이후에 longjmp()를 호출하면 저장해 놓았던 정보로 돌아가는 방식으로 작동한다고 한다.
    • 그래서 normal mode의 bash의 상태를 setjmp() 해놓고, 그 이후에 here_doc mode가 되면 SIGINT의 handler를 longjmp가 있는 함수로 바꾸어서 처리한다고 보면 될 것 같다. 따로 process fork가 일어나는 부분은 없다고 한다.
profile
공부 내용 저장소

0개의 댓글