신호와 신호의 차이점은 무엇입니까?


143

우리가 여기에있는 앱에 여분의 신호 처리기를 추가하려고했으며 저자가 sigaction()다른 신호 처리기를 설정하는 데 사용했음을 알았 습니다. 나는 사용하려고했다 signal(). 관습에 따라 사용해야 sigaction()하지만 처음부터 글을 쓰고 있다면 어떤 것을 선택해야합니까?

답변:


167

sigaction()그렇게하지 말아야 할 강력한 이유가 없다면 사용하십시오 .

signal()인터페이스는 오래 전부터 사용 가능하므로 C 표준에 정의되어 있습니다. 그럼에도 불구하고 sigaction()명시 적으로 추가 된 플래그를 사용 sigaction()하여 이전 signal()동작 을 충실하게 시뮬레이션 할 수 없는 경우 바람직하지 않은 여러 가지 특성을 피할 수 있습니다.

  1. signal()함수는 현재 핸들러가 실행되는 동안 다른 신호의 도착을 (필요하게) 차단하지 않습니다. sigaction()현재 핸들러가 반환 될 때까지 다른 신호를 차단할 수 있습니다.
  2. signal()기능은 (대개) SIG_DFL거의 모든 신호에 대해 신호 동작을 (기본값)으로 다시 설정합니다 . 이는 signal()핸들러가 첫 번째 조치로 자체적으로 다시 설치되어야 함을 의미합니다 . 또한 신호가 감지되는 시간과 신호가 두 번째 인스턴스에 도달하면 기본 동작 (일반적으로 종료, 때로는 편견-일명 코어 덤프)이 발생하는 처리기가 다시 설치되는 시간 사이에 취약성의 창을 엽니 다.
  3. signal()시스템 의 정확한 동작은 시스템마다 다르며 표준에서는 이러한 변형을 허용합니다.

이것들은 일반적으로 sigaction()대신에 사용 하는 좋은 이유 입니다 signal(). 그러나의 인터페이스 sigaction()는 명백하게 더 어리 석다.

사용하는 두하든, 같은 다른 신호 인터페이스에 의해 유혹하지 말고 sighold(), sigignore(), sigpause()sigrelse(). 그것들은 명목상에 대한 대안 sigaction()이지만, 거의 표준화되지 않았으며 POSIX에는 심각하게 사용하기보다는 이전 버전과의 호환성을 위해 존재합니다. POSIX 표준에 따르면 멀티 스레드 프로그램에서의 동작은 정의되어 있지 않습니다.

멀티 스레드 프로그램과 신호는 완전히 다른 복잡한 이야기입니다. AFAIK, signal()그리고 sigaction()멀티 스레드 응용 프로그램에서 모두 정상입니다.

Cornstalks는 다음을 관찰합니다 .

리눅스 매뉴얼 페이지 signal()는 다음 과 같이 말합니다 :

  signal()다중 스레드 프로세스에서 의 효과 는 지정되지 않았습니다.

따라서 sigaction()멀티 스레드 프로세스에서 안전하게 사용할 수있는 유일한 제품 이라고 생각 합니다.

그 흥미 롭군요. 이 경우 Linux 매뉴얼 페이지는 POSIX보다 제한적입니다. POSIX는 다음을 지정합니다 signal().

프로세스가 다중 스레드이거나 프로세스가 단일 스레드이고 다음과 같은 결과 이외의 신호 처리기가 실행되는 경우 :

  • 프로세스 호출은 abort(), raise(), kill(), pthread_kill(), 또는 sigqueue()차단되지 않은 신호를 생성하도록
  • 보류중인 신호가 차단 해제되고 차단 해제 된 통화 전에 전달되고 있음

신호 처리기 errno가로 선언 된 객체에 값을 할당하는 것 이외의 정적 저장 기간이 아닌 다른 객체 volatile sig_atomic_t를 참조하거나 신호 처리기가이 표준에 정의 된 함수 중 하나가 아닌 다른 함수를 호출하는 경우 동작이 정의되지 않습니다. 신호 개념 .

따라서 POSIX signal()는 멀티 스레드 응용 프로그램에서 의 동작을 명확하게 지정합니다 .

그럼에도 불구하고 sigaction()본질적으로 모든 상황에서 선호되며 휴대용 멀티 스레드 코드는 sigaction()"표준 C로 정의 된 함수 만 사용"과 같은 압도적 인 이유가 없다면 사용 하지 않아야합니다. 예, C11 코드는 다중 일 수 있습니다 스레드). 기본적 으로이 답변의 첫 단락에서 말하는 것입니다.


12
이 설명 signal은 실제로 Unix System V 동작에 대한 것입니다. POSIX는이 동작 또는 훨씬 더 정상적인 BSD 동작을 허용하지만 어떤 것을 얻을지 확신 할 수 없으므로 여전히 사용하는 것이 가장 좋습니다 sigaction.
R .. GitHub 중지 지원 얼음

1
sigaction ()에 명시 적으로 추가 된 플래그를 사용하여 이전 signal () 동작을 충실하게 시뮬레이션 할 수 있습니다. 어떤 플래그가 (특별히) 있을까요?
ChristianCuevas 1

@AlexFritz : 주로 SA_RESETHANDSA_NODEFER.
Jonathan Leffler 1

2
@BulatM. 를 사용할 수없는 경우 sigaction()기본적으로에 대한 표준 C 사양을 사용해야합니다 signal(). 그러나 그것은 당신이 할 수있는 일에 대해 매우 빈곤 한 옵션 세트를 제공합니다. 유형의 변수를 수정 (파일 범위) 할 수 있습니다 volatile sig_atomic_t. '빠른 종료'기능 중 하나를 호출하십시오 ( _Exit(), quick_exit()) 또는 abort(); signal()신호 인수로 현재 신호 번호로 호출 하십시오. 반환. 그리고 그게 다야. 다른 어떤 것도 휴대용으로 보장되지 않습니다. 그것은 대부분의 사람들이 그러한 규칙을 무시하기에 너무 엄격하지만 결과 코드는 복잡합니다.
Jonathan Leffler

1
sigaction()GCC 자체의 훌륭한 데모 : gnu.org/software/libc/manual/html_node/… ; 뛰어난 signal()데모 GCC에서 자신 : gnu.org/software/libc/manual/html_node/... . 에 통지 signal데모가 무시에서 핸들러 (변경하지 마십시오 SIG_IGN그게 이전에 의도적으로 설정 한 거라면).
Gabriel Staples

8

나 에게이 아래 줄은 결정하기에 충분했습니다.

sigaction () 함수는 신호를 제어하기위한보다 포괄적이고 안정적인 메커니즘을 제공합니다. 새로운 응용 프로그램은 signal () 대신 sigaction ()을 사용해야합니다

http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html#tag_03_690_07

처음부터 시작하거나 이전 프로그램을 수정하든 sigaction이 올바른 옵션이어야합니다.


5

OS의 신호 기능을위한 다른 인터페이스입니다. signal ()에 구현 정의 (종종 경쟁이 발생하기 쉬운) 동작이 있고 Windows, OS X, Linux 및 기타 UNIX 시스템에서 다르게 동작하므로 가능하면 신호를 보내려면 신호를 사용하는 것이 좋습니다.

자세한 내용은이 보안 정보를 참조하십시오.


1
방금 glibc 소스 코드를 보았고 signal ()은 sigaction ()을 호출했습니다. 위의 MacOS 매뉴얼 페이지에서도 같은 내용을 참조하십시오.
bmdhacks

그것은 알아두면 좋아. 종료하기 전에 신호 처리기가 깔끔하게 닫히는 데 사용되는 신호 처리기 만 보았으므로 처리기 재설치와 관련된 동작에 의존하지 않습니다.
Matthew Smith

5

signal ()은 표준 C이고 sigaction ()은 아닙니다.

POSIX 시스템 중 하나를 사용할 수 있다면 sigaction (); signal ()이 핸들러를 재설정하는지 여부는 지정되어 있지 않습니다. 이식성을 갖기 위해서는 핸들러 내부에서 signal ()을 다시 호출해야합니다. 더 나쁜 것은 경쟁이 있다는 것입니다. 두 개의 신호가 빠르게 연속해서 처리되고 두 번째 신호가 처리기를 다시 설치하기 전에 전달되는 경우 기본 동작이 발생하므로 프로세스가 종료 될 수 있습니다. 반면에 sigaction () 은 "신뢰할 수있는"신호 의미를 사용하도록 보장됩니다. 핸들러는 재설정되지 않으므로 핸들러를 다시 설치할 필요가 없습니다. SA_RESTART를 사용하면 일부 시스템 호출이 자동으로 다시 시작되도록 할 수 있으므로 수동으로 EINTR을 확인할 필요가 없습니다. sigaction () 더 많은 옵션이 있고 신뢰할 수 있으므로 사용하는 것이 좋습니다.

Psst ... 누구에게도 말하지 않았지만 POSIX에는 현재 bsd_signal () 함수가 있는데 signal ()처럼 작동하지만 BSD 의미를 제공하므로 신뢰할 수 있습니다. 주요 용도는 신뢰할 수있는 신호를 가정 한 오래된 응용 프로그램을 이식하는 데 사용되며 POSIX는 사용하지 않는 것이 좋습니다.


POSIX에는 함수가 없습니다. bsd_signal()일부 POSIX 구현에는 함수가있을 수 있지만 POSIX 자체에는 그러한 함수가 없습니다 ( POSIX 참조 ).
Jonathan Leffler

4

한마디로 :

sigaction()훌륭하고 잘 정의되어 있지만 Linux 기능이므로 Linux에서만 작동합니다. signal()나쁘고 잘못 정의되어 있지만 C 표준 함수이므로 모든 기능이 작동합니다.

리눅스 매뉴얼 페이지에 관해 무엇을 말해야합니까?

man 2 signal( 여기에서 온라인 참조 ) 상태 :

signal ()의 동작은 UNIX 버전에 따라 다르며 역사적으로 Linux 버전에 따라 다릅니다. 사용을 피하십시오 : sigaction(2)대신 사용하십시오 . 아래의 이식성을 참조하십시오.

이식성 signal ()의 휴대용은 신호의 배치를 SIG_DFL 또는 SIG_IGN으로 설정하는 것입니다. signal ()을 사용하여 신호 처리기를 설정할 때의 의미는 시스템마다 다릅니다 (및 POSIX.1은이 변형을 명시 적으로 허용합니다). 이 목적으로 사용하지 마십시오.

즉, 사용하지 마십시오 signal(). sigaction()대신 사용하십시오 !

GCC는 어떻게 생각합니까?

호환성 참고 :에 대해 위에서 말한 것처럼 signal이 기능은 가능하면 피해야합니다. sigaction선호되는 방법입니다.

출처 : https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling

따라서 리눅스와 GCC가 모두 signal()사용 sigaction()하지 말고 대신 사용한다고 말하면 질문을 제기합니다.이 혼란스러운 sigaction()것을 어떻게 사용합니까 !?

사용 예 :

https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handlingsignal() 에서 GCC의 탁월한 예제를 읽으 십시오.

그리고 그들의 훌륭한 sigaction()예는 다음과 같습니다. https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html

그 페이지를 읽은 후 다음과 같은 기술을 생각해 냈습니다 sigaction().

1. sigaction()위에서 설명한 것처럼 신호 처리기를 연결하는 올바른 방법이므로 다음과 같이하십시오.

#include <errno.h>  // errno
#include <signal.h> // sigaction()
#include <stdio.h>  // printf()
#include <string.h> // strerror()

#define LOG_LOCATION __FILE__, __LINE__, __func__ // Format: const char *, unsigned int, const char *
#define LOG_FORMAT_STR "file: %s, line: %u, func: %s: "

/// @brief      Callback function to handle termination signals, such as Ctrl + C
/// @param[in]  signal  Signal number of the signal being handled by this callback function
/// @return     None
static void termination_handler(const int signal)
{
    switch (signal)
    {
    case SIGINT:
        printf("\nSIGINT (%i) (Ctrl + C) signal caught.\n", signal);
        break;
    case SIGTERM:
        printf("\nSIGTERM (%i) (default `kill` or `killall`) signal caught.\n", signal);
        break;
    case SIGHUP:
        printf("\nSIGHUP (%i) (\"hang-up\") signal caught.\n", signal);
        break;
    default:
        printf("\nUnk signal (%i) caught.\n", signal);
        break;
    }

    // DO PROGRAM CLEANUP HERE, such as freeing memory, closing files, etc.


    exit(signal);
}

/// @brief      Set a new signal handler action for a given signal
/// @details    Only update the signals with our custom handler if they are NOT set to "signal ignore" (`SIG_IGN`),
///             which means they are currently intentionally ignored. GCC recommends this "because non-job-control
///             shells often ignore certain signals when starting children, and it is important for children
///             to respect this." See
///             https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
///             and https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html.
///             Note that termination signals can be found here:
///             https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
/// @param[in]  signal  Signal to set to this action
/// @param[in]  action  Pointer to sigaction struct, including the callback function inside it, to attach to this signal
/// @return     None
static inline void set_sigaction(int signal, const struct sigaction *action)
{
    struct sigaction old_action;

    // check current signal handler action to see if it's set to SIGNAL IGNORE
    sigaction(signal, NULL, &old_action);
    if (old_action.sa_handler != SIG_IGN)
    {
        // set new signal handler action to what we want
        int ret_code = sigaction(signal, action, NULL);
        if (ret_code == -1)
        {
            printf(LOG_FORMAT_STR "sigaction failed when setting signal to %i;\n"
                   "  errno = %i: %s\n", LOG_LOCATION, signal, errno, strerror(errno));
        }
    }
}

int main(int argc, char *argv[])
{
    //...

    // Register callbacks to handle kill signals; prefer the Linux function `sigaction()` over the C function
    // `signal()`: "It is better to use sigaction if it is available since the results are much more reliable."
    // Source: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
    // and /programming/231912/what-is-the-difference-between-sigaction-and-signal/232711#232711.
    // See here for official gcc `sigaction()` demo, which this code is modeled after:
    // https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html

    // Set up the structure to specify the new action, per GCC's demo.
    struct sigaction new_action;
    new_action.sa_handler = termination_handler; // set callback function
    sigemptyset(&new_action.sa_mask);
    new_action.sa_flags = 0;

    // SIGINT: ie: Ctrl + C kill signal
    set_sigaction(SIGINT, &new_action);
    // SIGTERM: termination signal--the default generated by `kill` and `killall`
    set_sigaction(SIGTERM, &new_action);
    // SIGHUP: "hang-up" signal due to lost connection
    set_sigaction(SIGHUP, &new_action);

    //...
}

그리고 signal()위에서 설명한 것처럼 신호 처리기를 연결하는 좋은 방법은 아니지만 사용 방법을 아는 것이 좋습니다.

다음과 같이 GCC 데모 코드를 복사하여 붙여 넣습니다.

#include <signal.h>

void
termination_handler (int signum)
{
  struct temp_file *p;

  for (p = temp_file_list; p; p = p->next)
    unlink (p->name);
}

int
main (void)
{
  
  if (signal (SIGINT, termination_handler) == SIG_IGN)
    signal (SIGINT, SIG_IGN);
  if (signal (SIGHUP, termination_handler) == SIG_IGN)
    signal (SIGHUP, SIG_IGN);
  if (signal (SIGTERM, termination_handler) == SIG_IGN)
    signal (SIGTERM, SIG_IGN);
  
}

알아야 할 주요 링크 :

  1. 표준 신호 : https://www.gnu.org/software/libc/manual/html_node/Standard-Signals.html#Standard-Signals
    1. 종료 신호 : https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
  2. 공식 GCC signal()사용 예를 포함한 기본 신호 처리 : https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
  3. 공식 GCC sigaction()사용 예 : https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
  4. 포함한 신호 세트 sigemptyset()sigfillset(); 나는 여전히 이것을 정확하게 이해하지 못하지만 그것들이 중요하다는 것을 알고 있습니다 : https://www.gnu.org/software/libc/manual/html_node/Signal-Sets.html

또한보십시오:

  1. TutorialsPoint C ++ 신호 처리 [우수한 데모 코드 사용] : https://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm
  2. https://www.tutorialspoint.com/c_standard_library/signal_h.htm

2

로부터 signal(3)매뉴얼 페이지

기술

 This signal() facility is a simplified interface to the more
 general sigaction(2) facility.

둘 다 동일한 기본 기능을 호출합니다. 아마도 단일 신호로 응답을 조작해서는 안되지만, 그것들을 혼합하면 아무것도 끊어지지 않아야합니다 ...


그건 내 맨 페이지에 없습니다! 내가 얻는 것은 "DESCRIPTION"입니다. signal () 시스템 호출은 숫자 signum으로 신호에 대한 새로운 신호 처리기를 설치합니다. " 유용한 매뉴얼 페이지 패키지 로 업그레이드해야 합니다.
Matthew Smith

1
그것은 Mac OS X 10.5 페이지에서 벗어났습니다.
dmckee --- 전 운영자 고양이

또한 glibc의 소스 코드에서 확인되었습니다. 신호 () 만은 sigaction ()를 호출
bmdhacks에게

2
그러나 이것은 모든 신호 구현에서 사실이 아닙니다. "시그니처"동작을 명령하려면이 가정에 의존하지 마십시오.
Ben Burns

1

또한 signal () 대신 sigaction ()을 사용하는 것이 좋으며 포인트를 하나 더 추가하고 싶습니다. sigaction ()은 죽었던 프로세스의 pid와 같은 더 많은 옵션을 제공합니다 (siginfo_t 구조체를 사용하여 가능).


0

이론 상으로는 더 이식성이 좋기 때문에 signal ()을 사용합니다. POSIX 호환성 레이어가없고 signal ()을 지원하는 최신 시스템을 제안 할 수있는 모든 의견을 제시합니다.

GLIBC 문서 에서 인용 :

단일 프로그램 내에서 신호 및 신호 기능을 모두 사용할 수 있지만 약간 이상한 방식으로 상호 작용할 수 있으므로주의해야합니다.

sigaction 함수는 signal 함수보다 더 많은 정보를 지정하므로 signal의 반환 값이 전체 범위의 sigaction 가능성을 표현할 수 없습니다. 따라서 신호를 사용하여 조치를 저장 한 후 나중에 다시 설정하면 sigaction으로 설정된 핸들러를 올바르게 다시 설정하지 못할 수 있습니다.

결과적으로 문제점이 발생하지 않도록하려면 프로그램에서 sigaction을 전혀 사용하지 않는 경우 sigaction을 사용하여 핸들러를 저장하고 복원하십시오. sigaction이 더 일반적이기 때문에 원래 signal 또는 sigaction으로 설정되었는지 여부에 관계없이 모든 조치를 올바르게 저장하고 다시 설정할 수 있습니다.

일부 시스템에서 신호를 사용하여 조치를 설정 한 후 sigaction을 사용하여 조사하는 경우, 처리기 주소는 신호로 지정한 것과 다를 수 있습니다. 신호가있는 동작 인수로 사용하기에 적합하지 않을 수도 있습니다. 그러나 당신은 그것을 sigaction에 대한 논쟁으로 사용할 수 있습니다. GNU 시스템에서는이 문제가 발생하지 않습니다.

따라서 단일 프로그램 내에서 하나 또는 다른 메커니즘을 일관되게 사용하는 것이 좋습니다.

이식성 참고 : 기본 신호 기능은 ISO C의 기능이며, sigaction은 POSIX.1 표준의 일부입니다. 비 POSIX 시스템으로의 이식성이 염려되는 경우 신호 기능을 대신 사용해야합니다.

저작권 (C) 1996-2008 Free Software Foundation, Inc.

GNU Free Documentation License, 버전 1.2 또는 Free Software Foundation에서 게시 한 이후 버전의 조항에 따라이 문서를 복사, 배포 및 / 또는 수정할 수있는 권한이 부여됩니다. 변하지 않는 섹션, 앞 표지 텍스트 및 뒷 표지 텍스트가 없습니다. 라이센스 사본은 "GNU Free Documentation License"섹션에 포함되어 있습니다.


0

맨 페이지 신호에서 (7)

프로세스 지향 신호는 현재 신호가 차단되지 않은 스레드 중 하나로 전달 될 수 있습니다. 둘 이상의 스레드가 신호를 차단하지 않은 경우 커널은 신호를 전달할 임의의 스레드를 선택합니다.

그리고이 "문제"는 signal (2)sigaction (2)에 존재한다고 말하고 싶습니다 . 따라서 신호와 pthread에주의하십시오.

... 및 signal (2) 은 glibc를 사용하여 Linux에서 sigaction (2) 을 호출하는 것으로 보입니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.