Linux에서 비동기 신호 핸들러는 어떻게 실행됩니까?
Linux에서 비동기 신호 핸들러의 실행이 정확히 어떻게 작동하는지 알고 싶습니다.첫째, 어떤 스레드가 신호 핸들러를 실행하는지 불분명합니다.둘째, 스레드에서 신호 핸들러를 실행하기 위한 절차를 알고 싶습니다.
첫 번째 문제에 대해, 나는 상반된 것처럼 보이는 두 가지 다른 설명을 읽었다.
Linux Kernel by Andries Broouber, § 5.2 "Receiving signals"에는 다음과 같이 기술되어 있습니다.
신호가 도착하면 프로세스가 중단되고 현재 레지스터가 저장되며 신호 핸들러가 호출됩니다.신호 핸들러가 돌아오면 중단된 액티비티가 계속됩니다.
StackOverflow 질문 "멀티 스레드 프로그램에서의 비동기 신호 처리"는 Linux의 동작을 SCO Unix와 비슷하다고 생각하게 합니다.
신호가 프로세스에 전달될 때 신호가 포착되면 다음 조건 중 하나를 충족하는 스레드 중 하나만 처리됩니다.
기타 고려사항:
또, Moshe Bar의 「Linux Signals Handling Model」에는 「비동기 신호는 신호를 차단하지 않고 발견된 첫 번째 스레드에 전달된다」라고 기재되어 있어 신호를 포함하지 않는 시그마스크가 있는 스레드에 전달되는 것을 의미한다.
어떤 게 맞습니까?
두 번째는 선택한 스레드의 스택과 등록 내용은 어떻게 됩니까?thread-to-run-the-signal-handler T가 실행 중에 있다고 가정합니다.do_stuff()
기능.스레드 T의 스택이 신호 핸들러 실행에 직접 사용됩니까(즉, 신호 트램펄린의 주소가 T의 스택에 푸시되고 제어 흐름이 신호 핸들러로 이동함)?또는 별도의 스택을 사용합니까?어떻게 작동합니까?
Linux 해커들이 스레드와 프로세스의 차이에 대해 혼동하는 경향이 있다는 사실을 고려한다면 이 두 가지 설명은 서로 모순되지 않습니다.주로 스레드가 메모리를 공유하는 프로세스로 구현될 수 있는 것처럼 가장하려고 했던 역사적 실수 때문입니다. :-)
그렇듯이 설명 #2가 훨씬 더 상세하고 완전하며 정확합니다.
스택 및 레지스터 내용에 대해서는 각 스레드가 자신의 대체 신호 처리 스택을 등록할 수 있으며, 프로세스는 신호 단위로 대체 신호 처리 스택에서 어떤 신호가 전달될지를 선택할 수 있다.인터럽트된 컨텍스트(레지스터, 신호 마스크 등)는 에 저장됩니다.ucontext_t
트램펄린 반환 주소와 함께 스레드용 (대체) 스택의 구조체.와 함께 설치된 신호 핸들러SA_SIGINFO
플래그는 이것을 조사할 수 있습니다.ucontext_t
구조물이 마음에 들지만, 그것을 가지고 할 수 있는 유일한 것은 저장된 신호 마스크를 검사하는 것(그리고 가능한 한 수정하는 것)입니다. (표준에 의해 수정이 허가되는지는 잘 모르겠지만, 예를 들어 신호 핸들러가 인터럽트된 코드의 신호 마스크를 원폭적으로 교환할 수 있기 때문에 매우 유용합니다.다시 발생하지 않도록 차단합니다.)
소스 #1(Andries Brower)은 싱글 스레드 프로세스에 적합합니다.Linux는 sigwait(2)의 스레드를 선호하지 않기 때문에 Source #2(SCO Unix)는 Linux에 적합하지 않습니다.사용 가능한 첫 번째 스레드에 대해서는 Moshe Bar가 맞습니다.
어느 나사산이 신호를 받습니까?Linux의 매뉴얼 페이지가 참고가 됩니다.프로세스에서는 CLONE_THREAD와 함께 clone(2)을 사용하여 여러 스레드를 만듭니다.이러한 스레드는 "스레드 그룹"에 속하며 단일 프로세스 ID를 공유합니다.clone(2) 매뉴얼에는 다음과 같이 기재되어 있습니다.
신호는 kill(2)을 사용하여 스레드 그룹 전체(즉, TGID)로 보내지거나 tgkill(2)을 사용하여 특정 스레드(즉, TID)로 보내질 수 있다.
신호 배치 및 처리는 프로세스 전체에 걸쳐 이루어집니다.처리되지 않은 신호가 스레드에 전달되면 스레드 그룹의 모든 멤버에게 영향을 줍니다(종료, 중지, 계속, 무시).
각 스레드에는 sigprocmask(2)에 의해 설정된 자체 신호 마스크가 있지만, 신호는 프로세스 전체(즉 스레드 그룹의 모든 멤버에게 전달 가능), kill(2)와 함께 전송될 경우, 또는 tgkill(2)과 함께 전송될 경우 개별 스레드에 대해 보류될 수 있습니다.sigpending (2)에 대한 호출은 프로세스 전체에 대해 보류 중인 신호와 호출 스레드에 대해 보류 중인 신호의 조합인 신호 세트를 반환합니다.
kill(2)을 사용하여 스레드 그룹에 신호를 전송하고 스레드 그룹에 해당 신호의 핸들러가 설치되어 있는 경우 핸들러는 신호를 차단하지 않은 스레드 그룹의 임의로 선택된 멤버 중 정확히 1개로 호출됩니다.그룹내의 복수의 스레드가 sigwaitinfo(2)를 사용해 같은 신호를 수신하는 것을 대기하고 있는 경우, 커널은 이러한 스레드 중 하나를 임의로 선택해, kill(2)를 사용해 송신되는 신호를 수신합니다.
Linux는 SCO Unix가 아닙니다.이는 일부 스레드가 신호를 대기하고 있는 경우(sigwaitinfo, sigtimedwait 또는 sigwait), 일부 스레드가 대기하고 있지 않은 경우라도 Linux는 모든 스레드에 신호를 제공할 수 있기 때문입니다.sigwaitinfo(2) 매뉴얼은 다음과 같이 경고합니다.
통상적인 사용방법에서는 발신 프로그램은 sigprocmask(2)에 대한 이전 콜을 통해 설정된 신호를 차단하고(이러한 신호의 디폴트 처리는 sigwaitinfo() 또는 sigtimedwait()에 대한 연속된 콜 사이에 보류 상태가 되어도 발생하지 않습니다), 이러한 신호의 핸들러를 확립하지 않습니다.멀티스레드 프로그램에서는 신호가 발신측 sigwaitinfo() 또는 sigtimedwait() 이외의 스레드에서 디폴트 처리에 따라 처리되지 않도록 모든 스레드에서 신호를 차단해야 합니다.
신호의 스레드를 선택하는 코드는 Linux/커널/signal.c에 있습니다(링크는 GitHub의 미러를 가리킵니다).wants_signal() 및 completes_signal() 함수를 참조하십시오.코드는 신호에 사용 가능한 첫 번째 스레드를 선택합니다.사용 가능한 스레드는 신호를 차단하지 않고 큐에 다른 신호가 없는 스레드입니다.코드는 메인 스레드를 먼저 확인한 후 다른 스레드를 내가 모르는 순서로 검사합니다.사용할 수 있는 스레드가 없는 경우 일부 스레드가 신호 블록을 해제하거나 큐를 비울 때까지 신호가 고착됩니다.
스레드가 신호를 받으면 어떻게 됩니까?신호 핸들러가 있는 경우 커널은 스레드가 핸들러를 호출하도록 합니다.대부분의 핸들러는 스레드의 스택에서 실행됩니다.프로세스가 sigaltstack(2)을 사용하여 스택을 제공하고 sigaction(2)과 SA_ONSTACK를 사용하여 핸들러를 설정하는 경우 핸들러는 대체 스택에서 실행할 수 있습니다.커널은 선택한 스택에 일부 항목을 푸시하고 스레드의 레지스터 중 일부를 설정합니다.
핸들러를 실행하려면 스레드가 사용자 공간에서 실행 중이어야 합니다.스레드가 커널에서 실행되고 있는 경우(시스템콜 또는 페이지 장애일 가능성이 있습니다), 사용자 공간에 도달할 때까지 핸들러는 실행되지 않습니다.커널은 일부 시스템 호출을 중단할 수 있으므로 스레드는 시스템 호출이 완료될 때까지 기다리지 않고 핸들러를 실행합니다.
신호 핸들러는 C 함수이므로 커널은 C 함수를 호출하는 아키텍처의 규칙을 따릅니다.arm, i386, powerpc, sparc 등의 아키텍처에는 각각 독자적인 규칙이 있습니다.powerpc의 경우, 커널은 핸들러(signum)를 호출하기 위해 레지스터 r3을 signum으로 설정합니다.또한 커널은 핸들러의 리턴 주소를 신호 트램펄린으로 설정합니다.반환 주소는 규칙에 따라 스택 또는 레지스터에 있습니다.
커널은 각 프로세스에 신호 트램펄린을 하나씩 넣습니다.이 트램펄린은 sigreturn(2)을 호출하여 스레드를 복원합니다.커널에서 sigreturn(2)은 스택에서 몇 가지 정보(저장된 레지스터 등)를 읽습니다.커널은 핸들러를 호출하기 전에 이 정보를 스택에 푸시했습니다.시스템 콜이 중단된 경우 커널은 콜을 재시작하거나(핸들러가 SA_RESTART를 사용한 경우에만), EINTR을 사용한 콜에 실패하거나 짧은 읽기 또는 쓰기를 반환할 수 있습니다.
언급URL : https://stackoverflow.com/questions/6949025/how-are-asynchronous-signal-handlers-executed-on-linux
'sourcecode' 카테고리의 다른 글
C++ - unistd를 포함합니다.왜 cunistd 안 돼? (0) | 2022.07.23 |
---|---|
C에 왼쪽이 없을 때 & 오퍼레이터는 어떻게 해야 합니까? (0) | 2022.07.23 |
math.h 헤더를 포함했는데도 "undefined reference to sqrt" 오류가 발생하는 이유는 무엇입니까? (0) | 2022.07.23 |
복사된 데이터가 변경될 때 Vuex 상태를 변경하지 않고 Vuex 상태에서 Data로 값을 복사하려면 어떻게 해야 합니까? (0) | 2022.07.17 |
특정 gcc 컴파일러의 glibc 버전을 확인합니다. (0) | 2022.07.17 |