신호는 내부적으로 어떻게 작동합니까?


31

일반적으로, 우리는 같은 신호 생성 프로세스를 종료한다 SIGKILL, SIGTSTP

그러나 누가 특정 신호를 주문했는지, 누가 특정 프로세스로 전송했는지, 그리고 일반적으로 신호가 어떻게 동작을 수행하는지는 어떻게 알 수 있습니까? 신호는 내부적으로 어떻게 작동합니까?


질문은 이해하기 조금 어렵습니다. 사과하고 무례하지 않습니다. 프로세스를 종료 한 명령을 실행 한 사람을 알고 싶거나 SIGKILL과 SIGSTP에 대해 더 알고 싶습니까?
pullsumo 2016 년

@mistermister 나는 누가 프로세스를 죽인 명령을 실행했는지 알고 싶습니다.
Varun Chhangani 2016 년

답변:


35

50,000 피트 뷰는 다음과 같습니다.

  1. 신호는 내부적으로 커널에 의해 (예를 들어, SIGSEGV유효하지 않은 주소에 액세스하거나 +를 SIGQUIT눌렀을 때 ) 또는 syscall (또는 여러 관련 주소 )을 사용하는 프로그램에 의해 생성됩니다 .Ctrl\kill

  2. syscall 중 하나 인 경우 커널은 호출 프로세스에 신호를 보낼 수있는 충분한 권한이 있는지 확인합니다. 그렇지 않으면 오류가 반환되고 신호가 발생하지 않습니다.

  3. 두 가지 특수 신호 중 하나 인 경우 대상 프로세스의 입력없이 커널이 무조건 작동합니다. 두 가지 특수 신호는 SIGKILL 및 SIGSTOP입니다. 기본 동작, 신호 차단 등에 대한 아래의 모든 내용은이 두 가지와 관련이 없습니다.

  4. 다음으로, 커널은 신호로 무엇을하는지 알아냅니다.

    1. 각 프로세스마다 각 신호와 관련된 동작이 있습니다. 이 기본 설정의 무리가 있으며, 프로그램은 다른 사람이 사용하여 설정할 수 있습니다 sigaction, signal등 이들은 "프로세스를 중지", "코어 덤프와 프로세스를 종료", "프로세스를 종료", "완전히 무시"등이 포함 기타

    2. 또한 프로그램은 신호별로 신호 전달 ( "차단")을 해제 할 수 있습니다. 그런 다음 신호는 차단 해제 될 때까지 보류 상태를 유지합니다.

    3. 프로그램은 커널이 자체적으로 조치를 취하는 대신 , 프로세스가 수행하는 모든 작업을 중단하고 지정된 함수를 호출 하여 신호를 프로세스에 동 기적으로 ( sigwait등을 사용 하여 signalfd) 또는 비동기 적으로 전달하도록 요청할 수 있습니다 .

"실시간 신호"라고하는 두 번째 신호 세트는 특별한 의미가 없으며 여러 신호를 대기시킬 수도 있습니다 (정상 신호는 신호가 차단 될 때 각 신호 중 하나만 대기합니다). 스레드가 서로 통신하기 위해 다중 스레드 프로그램에서 사용됩니다. 예를 들어 glibc의 POSIX 스레드 구현에 여러 가지가 사용됩니다. 또한 서로 다른 프로세스간에 통신하는 데 사용할 수도 있습니다 (예 : 여러 실시간 신호를 사용하여 fooctl 프로그램이 foo 데몬에 메시지를 보내도록 할 수 있음).

5 만 피트가 아닌 경우에는 man 7 signal커널 내부 문서 (또는 소스)를 사용해보십시오 .


"두 가지 특수 신호는 SIGKILL과 SIGSTOP"이므로 SIGCONT은 무엇
일까요

@HaukeLaging SIGCONT는 SIGSTOP을 취소하는 신호입니다. 이 문서에는 특별 문서가 나와 있지 않습니다 ... 기술적으로 프로세스가 무시하도록 설정할 수 있는지 확실하지 않은 경우 다시 시작할 수 없습니다 (SIGKILL 만 해당).
derobert

22

신호 구현은 매우 복잡하며 커널에 따라 다릅니다. 다시 말해, 다른 커널은 신호를 다르게 구현할 것입니다. 간단한 설명은 다음과 같습니다.

특수 레지스터 값을 기반으로하는 CPU는 실제로 벡터 테이블 인 "인터럽트 디스크립터 테이블"을 찾을 것으로 예상되는 메모리에 주소를 가지고 있습니다. 0으로 나누기 또는 INT 3 (디버그)과 같은 트랩과 같이 가능한 모든 예외에 대해 하나의 벡터가 있습니다. CPU에서 예외가 발생하면 플래그와 현재 명령어 포인터를 스택에 저장 한 다음 관련 벡터로 지정된 주소로 이동합니다. Linux에서이 벡터는 항상 예외 처리기가있는 커널을 가리 킵니다. 이제 CPU가 완료되고 Linux 커널이 대신합니다.

소프트웨어에서 예외를 트리거 할 수도 있습니다. 예를 들어, 사용자가 CTRL-를 누르면 C이 호출은 자체 예외 처리기를 호출하는 커널로 이동합니다. 일반적으로 핸들러에 도달하는 다른 방법이 있지만 동일한 기본 사항에 관계없이 컨텍스트가 스택에 저장되고 커널의 예외 핸들러로 이동합니다.

그런 다음 예외 처리기는 신호를 수신 할 스레드를 결정합니다. 0으로 나누기와 같은 것이 발생하면 쉽습니다. 예외를 발생시킨 스레드가 신호를 얻지 만 다른 유형의 신호의 경우 결정이 매우 복잡 할 수 있으며 비정상적인 경우 다소 임의의 스레드가 발생할 수 있습니다. 신호를 얻으십시오.

커널이하는 일을 신호로 보내려면 먼저 신호 유형 등을 나타내는 값을 설정하십시오 SIGHUP. 이것은 단지 정수입니다. 모든 프로세스에는이 값이 저장되는 "보류중인 신호"메모리 영역이 있습니다. 그런 다음 커널은 신호 정보로 데이터 구조를 만듭니다. 이 구조는 디폴트, 무시 또는 처리 될 수있는 신호 "처리"를 포함한다. 그런 다음 커널은 자체 함수를 호출합니다 do_signal(). 다음 단계가 시작됩니다.

do_signal()첫 번째는 여부를 결정 신호를 처리합니다. 예를 들어 그것이 killdo_signal()경우 프로세스 종료 프로세스를 종료합니다. 그렇지 않으면 처분을 봅니다. 배치가 기본값 인 do_signal()경우 신호에 따라 달라지는 기본 정책에 따라 신호 를 처리합니다. 처리가 처리되면, 사용자 프로그램에 해당 신호를 처리하도록 설계된 기능이 있으며이 기능에 대한 포인터가 위에서 언급 한 데이터 구조에 있음을 의미합니다. 이 경우 do_signal ()은 다른 커널 함수를 호출합니다.handle_signal()그런 다음 사용자 모드로 다시 전환하고이 함수를 호출하는 프로세스를 거칩니다. 이 핸드 오프의 세부 사항은 매우 복잡합니다. 프로그램의이 코드는 일반적으로의 기능을 사용할 때 프로그램에 자동으로 연결됩니다 signal.h.

보류중인 신호 값을 적절하게 검사하여 커널은 프로세스가 모든 신호를 처리하고 있는지 확인하고 그렇지 않은 경우 적절한 조치를 취하여 신호에 따라 프로세스를 휴면 상태로 만들거나 종료시킬 수 있습니다.


15

이 질문에 대한 답변이 있지만 Linux 커널에 자세한 이벤트 흐름을 게시하겠습니다.
이것은 Linux 게시물 : Linux Signals – 내부 (sklinuxblog.blogspot.in의“Linux 게시물”블로그)에서 완전히 복사 한 것입니다.

신호 사용자 공간 C 프로그램

간단한 신호 사용자 공간 C 프로그램을 작성하는 것으로 시작하겠습니다.

#include<signal.h>
#include<stdio.h>

/* Handler function */
void handler(int sig) {
    printf("Receive signal: %u\n", sig);
};

int main(void) {
    struct sigaction sig_a;

    /* Initialize the signal handler structure */
    sig_a.sa_handler = handler;
    sigemptyset(&sig_a.sa_mask);
    sig_a.sa_flags = 0;

    /* Assign a new handler function to the SIGINT signal */
    sigaction(SIGINT, &sig_a, NULL);

    /* Block and wait until a signal arrives */
    while (1) {
            sigsuspend(&sig_a.sa_mask);
            printf("loop\n");
    }
    return 0;
};

이 코드는 SIGINT 신호에 새로운 핸들러를 할당합니다. Ctrl+ C키 조합을 사용하여 SIGINT를 실행중인 프로세스로 보낼 수 있습니다 . 때 Ctrl+ C다음을 누르면 비동기 신호 SIGINT는 작업에 전송됩니다. kill -INT <pid>다른 터미널 에서 명령 을 보내는 것과 같습니다 .

당신이 할 경우 kill -l(즉, 소문자의 L"목록"의 약자) 당신은 실행중인 프로세스에 전송 될 수있는 다양한 신호를 알게됩니다.

[root@linux ~]# kill -l
 1) SIGHUP        2) SIGINT        3) SIGQUIT       4) SIGILL        5) SIGTRAP
 6) SIGABRT       7) SIGBUS        8) SIGFPE        9) SIGKILL      10) SIGUSR1
11) SIGSEGV      12) SIGUSR2      13) SIGPIPE      14) SIGALRM      15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD      18) SIGCONT      19) SIGSTOP      20) SIGTSTP
21) SIGTTIN      22) SIGTTOU      23) SIGURG       24) SIGXCPU      25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF      28) SIGWINCH     29) SIGIO        30) SIGPWR
31) SIGSYS       34) SIGRTMIN     35) SIGRTMIN+1   36) SIGRTMIN+2   37) SIGRTMIN+3
38) SIGRTMIN+4   39) SIGRTMIN+5   40) SIGRTMIN+6   41) SIGRTMIN+7   42) SIGRTMIN+8
43) SIGRTMIN+9   44) SIGRTMIN+10  45) SIGRTMIN+11  46) SIGRTMIN+12  47) SIGRTMIN+13
48) SIGRTMIN+14  49) SIGRTMIN+15  50) SIGRTMAX-14  51) SIGRTMAX-13  52) SIGRTMAX-12
53) SIGRTMAX-11  54) SIGRTMAX-10  55) SIGRTMAX-9   56) SIGRTMAX-8   57) SIGRTMAX-7
58) SIGRTMAX-6   59) SIGRTMAX-5   60) SIGRTMAX-4   61) SIGRTMAX-3   62) SIGRTMAX-2
63) SIGRTMAX-1   64) SIGRTMAX

또한 다음 키 조합을 사용하여 특정 신호를 보낼 수 있습니다.

  • Ctrl+ C– 응용 프로그램을 종료하는 기본 동작 인 SIGINT를 보냅니다.
  • Ctrl+ \  – 응용 프로그램 덤프 코어를 종료하는 기본 동작 인 SIGQUIT를 보냅니다.
  • Ctrl+Z – 프로그램을 일시 중단하는 SIGSTOP을 보냅니다.

위의 C 프로그램을 컴파일하고 실행하면 다음과 같은 결과가 나타납니다.

[root@linux signal]# ./a.out
Receive signal: 2
loop
Receive signal: 2
loop
^CReceive signal: 2
loop

와도 Ctrl + C또는 kill -2 <pid>프로세스가 종료되지 않습니다. 대신 신호 처리기를 실행하고 반환합니다.

신호가 프로세스로 전송되는 방법

신호의 내부가 프로세스로 전송되는 것을보고 dump_stack과 함께 Jprobe를 __send_signal함수에 놓으면 다음과 같은 호출 추적이 나타납니다.

May  5 16:18:37 linux kernel: dump_stack+0x19/0x1b
May  5 16:18:37 linux kernel: my_handler+0x29/0x30 (probe)
May  5 16:18:37 linux kernel: complete_signal+0x205/0x250
May  5 16:18:37 linux kernel: __send_signal+0x194/0x4b0
May  5 16:18:37 linux kernel: send_signal+0x3e/0x80
May  5 16:18:37 linux kernel: do_send_sig_info+0x52/0xa0
May  5 16:18:37 linux kernel: group_send_sig_info+0x46/0x50
May  5 16:18:37 linux kernel: __kill_pgrp_info+0x4d/0x80
May  5 16:18:37 linux kernel: kill_pgrp+0x35/0x50
May  5 16:18:37 linux kernel: n_tty_receive_char+0x42b/0xe30
May  5 16:18:37 linux kernel:  ? ftrace_ops_list_func+0x106/0x120
May  5 16:18:37 linux kernel: n_tty_receive_buf+0x1ac/0x470
May  5 16:18:37 linux kernel: flush_to_ldisc+0x109/0x160
May  5 16:18:37 linux kernel: process_one_work+0x17b/0x460
May  5 16:18:37 linux kernel: worker_thread+0x11b/0x400
May  5 16:18:37 linux kernel: rescuer_thread+0x400/0x400
May  5 16:18:37 linux kernel:  kthread+0xcf/0xe0
May  5 16:18:37 linux kernel:  kthread_create_on_node+0x140/0x140
May  5 16:18:37 linux kernel:  ret_from_fork+0x7c/0xb0
May  5 16:18:37 linux kernel: ? kthread_create_on_node+0x140/0x140

따라서 신호 전송을위한 주요 함수 호출은 다음과 같습니다.

First shell send the Ctrl+C signal using n_tty_receive_char
n_tty_receive_char()
isig()
kill_pgrp()
__kill_pgrp_info()
group_send_sig_info() -- for each PID in group call this function
do_send_sig_info()
send_signal()
__send_signal() -- allocates a signal structure and add to task pending signals
complete_signal()
signal_wake_up()
signal_wake_up_state()  -- sets TIF_SIGPENDING in the task_struct flags. Then it wake up the thread to which signal was delivered.

이제 모든 것이 설정되고 필요한 변경이 task_struct 프로세스에 .

신호 처리

신호는 시스템 호출에서 리턴 될 때 또는 인터럽트에서 리턴 할 때 프로세스에 의해 점검 / 처리됩니다. 시스템 호출로부터의 리턴은 파일에 있습니다.entry_64.S .

int_signal 함수가 entry_64.S호출되어 함수를 호출합니다.do_notify_resume() .

기능을 확인합시다 do_notify_resume(). 이 함수는 TIF_SIGPENDING플래그가 다음에 설정되어 있는지 확인 합니다 task_struct.

 /* deal with pending signal delivery */
 if (thread_info_flags & _TIF_SIGPENDING)
  do_signal(regs);
do_signal calls handle_signal to call the signal specific handler
Signals are actually run in user mode in function:
__setup_rt_frame -- this sets up the instruction pointer to handler: regs->ip = (unsigned long) ksig->ka.sa.sa_handler;

시스템 호출 및 신호

"느린"syscall (예 : 읽기 / 쓰기 차단, 프로세스를 대기 상태로 전환) : TASK_INTERRUPTIBLE또는TASK_UNINTERRUPTIBLE .

상태에있는 작업은 신호에 의해 상태 TASK_INTERRUPTIBLE로 변경됩니다 TASK_RUNNING.TASK_RUNNING프로세스를 예약 할 수 있음을 의미합니다.

실행되면 "느린"syscall이 완료되기 전에 신호 처리기가 실행됩니다. 그만큼syscall 기본적으로 완료되지 않습니다.

만약 SA_RESTART플래그가 설정되어,syscall 신호 처리기 완료된 후 다시 시작된다.

참고 문헌


사이트에 기여하기 위해 노력해 주셔서 감사합니다. (1) 다른 사이트의 자료 (단어, 단어, 문자, 문법 및 구두점 오류 포함)를 복사하려는 경우 현재하고 있다고 말해야합니다. 훨씬 더 명확합니다. 필요한 경우 소스를 "참조"로 표시하는 것만으로는 충분하지 않습니다. 블로그 작성자가 아니라면 (K_K = sk?),이 경우 블로그에 링크 할 필요가 없습니다. 그러나 링크가 필요한 경우 자신의 블로그임을 공개 해야합니다 . … (계속)
G-Man, 'Reinstate

(계속)… (2) 귀하의 출처 (방문한 블로그)가 좋지 않습니다. 질문을받은 지 4 년이 지났습니다. 더 나은 참조를 찾을 수 없습니까? (원래 작성자 인 경우 죄송합니다.) 위에서 언급 한 문법 및 문장 부호 오류 (일반적으로 엉성한 문구 및 형식이 잘못됨) 외에도 잘못된 것입니다. (2a) Ctrl + Z는 SIGSTOP이 아닌 SIGTSTP를 전송합니다. (SIGTERM과 같은 SIGTSTP는 잡힐 수 있습니다. SIGKILL과 같은 SIGSTOP은 할 수 없습니다.)… (계속)
G-Man은 '복귀 국 모니카'

(계속)… (2b) 쉘은 Ctrl + C 신호를 보내지 않습니다. 쉘은 신호를 보내는 데 아무런 역할을하지 않습니다 (사용자가 kill쉘 내장 명령 인 경우 제외 ). (2c) }함수를 닫은 후 세미콜론 은 엄격하게 말해서 오류는 아니지만 불필요하고 정통하지 않습니다. (3) 모든 것이 정확하더라도 그 질문에 대한 답은 그리 좋지 않을 것입니다. (3a) 질문은 다소 불분명하지만 행위자 (사용자 및 프로세스) 신호를 시작 (즉, 전송 ) 하는 방법에 초점을 맞추고있는 것 같습니다 . … (계속)
G-Man, 'Reinstate

(계속)… 대답은 커널이 생성 한 신호 (특히 키보드 생성 신호)와 수신자 프로세스가 신호에 반응하는 방식에 초점을 맞추는 것 같습니다. (3b) 질문은«누가 내 프로세스를 죽였는가? 누가 어떻게했는지, 어떻게?»의 수준에있는 것으로 보입니다. 대답은 신호 처리 API, 커널 루틴, 커널 디버깅 (Jprobe?), 커널 스택 추적 및 커널 데이터 구조. IMO는 부적절하게 낮은 수준입니다. 특히 독자가 이러한 내부 작업에 대해 더 많이 배울 수있는 참조를 제공하지 않기 때문에 부적절합니다.
G-Man, 'Reinstate

1
그것은 내 자신의 블로그 .. 내 자신의 흔적 .. 그게 내가 원하는 것 .. 모두가 그런 세부 흐름을 알고 있어야합니다. channel .. 이것은 문법 내부가 아닌 커널 내부 답변입니다.
K_K
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.