신호 처리기에서 printf를 사용하지 않는 방법은 무엇입니까?


86

printf재진입이 아니기 때문에 시그널 핸들러에서 사용하는 것은 안전하지 않습니다. 하지만 printf이런 식으로 사용하는 예제 코드를 많이 보았습니다 .

그래서 제 질문은 printf신호 처리기에서 사용을 피해야 할 때이며 권장되는 대체품이 있습니까?


12
제목에있는 질문에 대한 간단하고 그다지 유용하지 않은 답변 : printf신호 처리자에서 호출이 보이십니까? 그것을 삭제하십시오.
Keith Thompson

6
안녕 Yu Hao! 나는 당신이 읽기에 매우 흥미로운 링크를 찾을 것이라고 생각합니다. "안전한 신호 처리를 위해 재진입 기능을 사용하십시오" 나는 오랫동안 읽었습니다. 여기에서 인공적인 것을 여러분과 공유하고 싶습니다. 즐기시기 바랍니다.
Grijesh 차우

답변:


58

일부 플래그 변수를 사용하고 시그널 핸들러 내에서 해당 플래그를 설정하고 printf()정상적인 작동 중에 main () 또는 프로그램의 다른 부분에서 해당 플래그 호출 함수를 기반으로 할 수 있습니다.

printf신호 처리기 내에서 와 같은 모든 함수를 호출하는 것은 안전하지 않습니다 . 유용한 기술은 신호 처리기를 사용하여 a를 설정 한 flag다음 flag 주 프로그램에서 확인하고 필요한 경우 메시지를 인쇄하는 것입니다.

아래 예제에서 신호 처리기 ding () alarm_fired은 SIGALRM이 포착되고 주 함수 alarm_fired값 에서 printf를 조건부로 올바르게 호출하기 위해 검사 될 때 플래그 를 1로 설정 합니다.

static int alarm_fired = 0;
void ding(int sig) // can be called asynchronously
{
  alarm_fired = 1; // set flag
}
int main()
{
    pid_t pid;
    printf("alarm application starting\n");
    pid = fork();
    switch(pid) {
        case -1:
            /* Failure */
            perror("fork failed");
            exit(1);
        case 0:
            /* child */
            sleep(5);
            kill(getppid(), SIGALRM);
            exit(0);
    }
    /* if we get here we are the parent process */
    printf("waiting for alarm to go off\n");
    (void) signal(SIGALRM, ding);
    pause();
    if (alarm_fired)  // check flag to call printf
      printf("Ding!\n");
    printf("done\n");
    exit(0);
}

참조 : Beginning Linux Programming, 4th Edition ,이 책에서는 코드가 정확히 설명되어 있습니다 (원하는 것), 11 장 : 프로세스 및 신호, 484 페이지

또한 비동기 적으로 호출 할 수 있으므로 핸들러 함수를 작성할 때 특별한주의가 필요합니다. 즉, 처리기는 프로그램의 어느 지점에서나 예기치 않게 호출 될 수 있습니다. 매우 짧은 간격 동안 두 신호가 도착하면 하나의 핸들러가 다른 핸들러 내에서 실행될 수 있습니다. 그리고 선언하는 것이 더 좋은 방법으로 간주됩니다 volatile sigatomic_t.이 유형은 항상 원자 적으로 액세스되며 변수에 대한 액세스 중단에 대한 불확실성을 방지합니다. ( 자세한 만료를 위해 원자 데이터 액세스 및 신호 처리 읽기 ).

신호 처리기 정의 읽기 : signal()또는 sigaction()함수 로 설정할 수있는 신호 처리기 함수를 작성하는 방법을 배웁니다 . 매뉴얼 페이지
의 인증 된 기능 목록 , 신호 처리기 내에서이 함수를 호출하는 것은 안전합니다.


18
그것은 선언에 더 연습으로 간주됩니다volatile sigatomic_t alarm_fired;
실레 Starynkevitch을


1
@GrijeshChauhan : 우리가 product 코드에서 작업하고 있다면 pause 함수를 호출 할 수없고, 신호가 발생할 때 흐름은 어디에나있을 수 있습니다. 그래서이 경우 우리는 "if (alarm_fired) printf ("Ding! \엔");" 코드에서.
판 카즈의 kushwaha

예 @pankajkushwaha, 당신이 올바른지, 그것은 경쟁 조건에서 고통입니다
Grijesh 차우

@GrijeshChauhan, 내가 이해할 수 없었던 두 가지가 있습니다. 1. 언제 깃발을 확인해야하는지 어떻게 알 수 있습니까? 따라서 인쇄 할 거의 모든 지점에서 코드에 여러 검사 점이있을 것입니다. 2. 시그널 등록 전에 시그널이 호출되거나 체크 포인트 이후에 시그널이 발생할 수있는 레이스 컨디션이있을 것입니다. 나는 이것이 일부 조건에서만 인쇄에 도움이 될 것이라고 생각하지만 문제를 완전히 해결하지는 못합니다.
Darshan b

52

주요 문제는 신호가 중단 malloc()되거나 유사한 기능 이있는 경우 사용 가능한 목록과 사용 된 목록 또는 기타 유사한 작업간에 메모리 블록을 이동하는 동안 내부 상태가 일시적으로 일치하지 않을 수 있다는 것입니다. 신호 처리기의 코드가 다음을 호출하는 함수를 호출하면malloc() 하면 메모리 관리가 완전히 망가질 수 있습니다.

C 표준은 신호 처리기에서 수행 할 수있는 작업에 대해 매우 보수적 인 관점을 취합니다.

ISO / IEC 9899 : 2011 §7.14.1.1 signal 기능

¶5 abort또는 raise함수 를 호출 한 결과가 아닌 다른 신호가 발생하는 경우, 신호 핸들러가 값을 할당하는 것 외에 잠금이없는 원자 객체가 아닌 정적 또는 스레드 저장 기간을 가진 객체를 참조하면 동작이 정의되지 않습니다. 로 선언 된 객체로 선언 volatile sig_atomic_t되거나, 신호 핸들러가 abort함수, _Exit함수, quick_exit함수 또는 signal함수를 호출 한 신호에 해당하는 신호 번호와 동일한 첫 번째 인수 를 사용하여 표준 라이브러리의 모든 함수를 호출합니다. 매니저. 또한 이러한 signal함수 호출로 인해 SIG_ERR반환이 발생하면의 값 errno이 불확실합니다. 252)

252) 비동기 신호 처리기에 의해 신호가 생성되면 동작이 정의되지 않습니다.

POSIX는 시그널 핸들러에서 할 수있는 일에 대해 훨씬 더 관대합니다.

POSIX 2008 에디션의 신호 개념 은 다음과 같이 말합니다.

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

  • 프로세스 호출은 abort(), raise(), kill(), pthread_kill(), 또는 sigqueue()차단되지 않은 신호를 생성하도록

  • 보류중인 신호가 차단 해제되고 차단 해제 된 호출이 반환되기 전에 전달됩니다.

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

다음 표는 비동기 신호 안전 기능 집합을 정의합니다. 따라서 응용 프로그램은 제한없이 신호 포착 함수에서이를 호출 할 수 있습니다.

_Exit()             fexecve()           posix_trace_event() sigprocmask()
_exit()             fork()              pselect()           sigqueue()
…
fcntl()             pipe()              sigpause()          write()
fdatasync()         poll()              sigpending()

위 표에없는 모든 기능은 신호와 관련하여 안전하지 않은 것으로 간주됩니다. 신호가있는 경우 POSIX.1-2008의이 볼륨에 정의 된 모든 기능은 신호 포착 기능에서 호출되거나 중단 될 때 정의 된대로 동작해야합니다. 단, 신호가 안전하지 않은 기능을 중단하고 신호- catching 함수는 안전하지 않은 함수를 호출하지만 동작은 정의되지 않았습니다.

값을 얻는 errno작업과 값을 할당하는 작업 errno은 비동기 신호 안전이어야합니다.

신호가 스레드에 전달 될 때 해당 신호의 동작이 종료, 중지 또는 계속을 지정하면 전체 프로세스가 각각 종료, 중지 또는 계속됩니다.

그러나, 그 printf() 함수 계열은 해당 목록에 특히없고 신호 처리기에서 안전하게 호출되지 않을 수 있습니다.

POSIX 2,016 업데이트를 포함하는 안전 기능의 목록을 연장, 특히 발 기능의 다수 <string.h>특히 유용한 추가이다 (또는 특히 초조 감독이었다). 이제 목록은 다음과 같습니다.

_Exit()              getppid()            sendmsg()            tcgetpgrp()
_exit()              getsockname()        sendto()             tcsendbreak()
abort()              getsockopt()         setgid()             tcsetattr()
accept()             getuid()             setpgid()            tcsetpgrp()
access()             htonl()              setsid()             time()
aio_error()          htons()              setsockopt()         timer_getoverrun()
aio_return()         kill()               setuid()             timer_gettime()
aio_suspend()        link()               shutdown()           timer_settime()
alarm()              linkat()             sigaction()          times()
bind()               listen()             sigaddset()          umask()
cfgetispeed()        longjmp()            sigdelset()          uname()
cfgetospeed()        lseek()              sigemptyset()        unlink()
cfsetispeed()        lstat()              sigfillset()         unlinkat()
cfsetospeed()        memccpy()            sigismember()        utime()
chdir()              memchr()             siglongjmp()         utimensat()
chmod()              memcmp()             signal()             utimes()
chown()              memcpy()             sigpause()           wait()
clock_gettime()      memmove()            sigpending()         waitpid()
close()              memset()             sigprocmask()        wcpcpy()
connect()            mkdir()              sigqueue()           wcpncpy()
creat()              mkdirat()            sigset()             wcscat()
dup()                mkfifo()             sigsuspend()         wcschr()
dup2()               mkfifoat()           sleep()              wcscmp()
execl()              mknod()              sockatmark()         wcscpy()
execle()             mknodat()            socket()             wcscspn()
execv()              ntohl()              socketpair()         wcslen()
execve()             ntohs()              stat()               wcsncat()
faccessat()          open()               stpcpy()             wcsncmp()
fchdir()             openat()             stpncpy()            wcsncpy()
fchmod()             pause()              strcat()             wcsnlen()
fchmodat()           pipe()               strchr()             wcspbrk()
fchown()             poll()               strcmp()             wcsrchr()
fchownat()           posix_trace_event()  strcpy()             wcsspn()
fcntl()              pselect()            strcspn()            wcsstr()
fdatasync()          pthread_kill()       strlen()             wcstok()
fexecve()            pthread_self()       strncat()            wmemchr()
ffs()                pthread_sigmask()    strncmp()            wmemcmp()
fork()               raise()              strncpy()            wmemcpy()
fstat()              read()               strnlen()            wmemmove()
fstatat()            readlink()           strpbrk()            wmemset()
fsync()              readlinkat()         strrchr()            write()
ftruncate()          recv()               strspn()
futimens()           recvfrom()           strstr()
getegid()            recvmsg()            strtok_r()
geteuid()            rename()             symlink()
getgid()             renameat()           symlinkat()
getgroups()          rmdir()              tcdrain()
getpeername()        select()             tcflow()
getpgrp()            sem_post()           tcflush()
getpid()             send()               tcgetattr()

결과적 write()으로 printf()et al이 제공하는 서식 지원없이 사용 하거나 코드의 적절한 위치에서 (주기적으로) 테스트하는 플래그를 설정하게됩니다. 이 기술은 훌륭하게에서 증명되고 대답 하여 Grijesh 차우 .


표준 C 기능 및 신호 안전

chqrlie 흥미로운 질문에 대해 부분적인 답변 만 제공합니다.

대부분의 문자열 함수 <string.h>또는 문자 클래스 함수의 출처 <ctype.h>와 더 많은 C 표준 라이브러리 함수가 위 목록에없는 이유는 무엇입니까? 구현은 strlen()시그널 핸들러에서 호출 하는 것을 안전하지 않게 만들기 위해 고의적으로 악해야 합니다.

의 많은 함수에 <string.h>대해 비동기 신호 안전으로 선언되지 않은 이유를 알기가 어렵습니다.strlen() 와 함께 대표적인 예이며 strchr(), strstr()등, 등 한편, 다른 기능 strtok(), strcoll()strxfrm()다소 복잡하고 비동기 신호에 안전하지 않을 수 있습니다. 때문에이 strtok()호출 사이에 상태를 유지하고, 신호 처리기 쉽게 사용하는 코드의 일부가 여부를 알 수없는 strtok()엉망이 될 것이다. strcoll()strxfrm()기능은 로케일에 민감한 데이터로 작업하고, 로케일을로드하는 것은 국가 설정의 모든 종류를 포함한다.

에서 기능 (매크로) <ctype.h>모든 로케일에 의존하며, 따라서 같은 문제로 실행 수 strcoll()strxfrm().

나는 하드에서 수학 함수는 이유를 찾을 <math.h>그들이 SIGFPE (부동 소수점 예외)에 의해 영향을받을 수 있기 때문에 유일한 시간에 대해 내가 그 요즘 볼 수 있지만 그것이 아닌 경우입니다, 비동기 시그널에 안전하지 않습니다 정수 0으로 나누기. 유사한 불확실성이 <complex.h>, <fenv.h>및 에서 발생합니다 <tgmath.h>.

예를 들어의 일부 기능은 <stdlib.h>제외 될 수 있습니다 abs(). 다른 것들은 특히 문제가 있습니다. malloc()그리고 가족이 대표적인 예입니다.

POSIX 환경에서 사용되는 Standard C (2011)의 다른 헤더에 대해서도 유사한 평가를 할 수 있습니다. (표준 C는 너무 제한적이어서 순수한 표준 C 환경에서 분석하는 데 관심이 없습니다.) '로케일 종속'으로 표시된 것은 로케일을 조작하는 데 메모리 할당 등이 필요할 수 있기 때문에 안전하지 않습니다.

  • <assert.h>아마도 안전하지 않을 것입니다.
  • <complex.h>아마도 안전함
  • <ctype.h> - 안전하지 않음
  • <errno.h> — 안전
  • <fenv.h>아마도 안전하지 않을 것입니다.
  • <float.h> — 기능 없음
  • <inttypes.h> — 로케일 구분 함수 (안전하지 않음)
  • <iso646.h> — 기능 없음
  • <limits.h> — 기능 없음
  • <locale.h> — 로케일 구분 함수 (안전하지 않음)
  • <math.h>아마도 안전함
  • <setjmp.h> - 안전하지 않음
  • <signal.h> — 허용됨
  • <stdalign.h> — 기능 없음
  • <stdarg.h> — 기능 없음
  • <stdatomic.h>안전 할 수 있지만 안전하지 않을 수 있음
  • <stdbool.h> — 기능 없음
  • <stddef.h> — 기능 없음
  • <stdint.h> — 기능 없음
  • <stdio.h> - 안전하지 않음
  • <stdlib.h> — 모두 안전하지는 않습니다 (일부는 허용되지만 다른 것은 허용되지 않음).
  • <stdnoreturn.h> — 기능 없음
  • <string.h> — 모두 안전하지 않음
  • <tgmath.h>아마도 안전함
  • <threads.h>아마도 안전하지 않을 것입니다.
  • <time.h> — 로케일에 따라 다름 (그러나 time() 명시 적으로 허용됨)
  • <uchar.h> — 로케일 종속
  • <wchar.h> — 로케일 종속
  • <wctype.h> — 로케일 종속

POSIX 헤더를 분석하는 것은… 많은 것이 있고 일부 기능은 안전 할 수 있지만 많은 것은 안전하지 않을 수 있다는 점에서 더 어려울 것입니다. 그러나 POSIX가 어떤 기능이 비동기 신호 안전인지 (많은 것은 아님)를 말하므로 더 간단합니다. 같은 헤더 <pthread.h>에는 세 가지 안전한 기능과 많은 안전하지 않은 기능이 있습니다.

NB : POSIX 환경에서 C 기능 및 헤더에 대한 거의 모든 평가는 반 교육을받은 추측입니다. 표준 기관의 결정적인 진술은 의미가 없습니다.


대부분의 문자열 함수 <string.h>또는 문자 클래스 함수의 출처 <ctype.h>와 더 많은 C 표준 라이브러리 함수가 위 목록에없는 이유는 무엇입니까? 구현은 strlen()시그널 핸들러에서 호출 하는 것을 안전하지 않게 만들기 위해 고의적으로 악해야 합니다.
chqrlie

@chqrlie : 흥미로운 질문 — 업데이트를 참조하십시오 (댓글에 현명하게 다룰 방법이 없었습니다).
Jonathan Leffler

심층 분석에 감사드립니다. <ctype.h>물건에 관해서 는 로케일 에 따라 다르며 신호가 로케일 설정 기능을 방해하면 문제를 일으킬 수 있지만 로케일이로드되면이를 사용하는 것이 안전해야합니다. 복잡한 상황에서는 로케일 데이터를 점진적으로로드하여 함수가 <ctype.h>안전하지 않게 만들 수 있다고 생각합니다 . 결론은 여전히 ​​남아 있습니다. 의심 스러우면 기권하십시오.
chqrlie

@chqrlie : 이야기의 도덕이 의심 스러울 때, 기권 해야한다는 데 동의합니다 . 좋은 요약입니다.
Jonathan Leffler

13

printf신호 처리기에서 사용을 피하는 방법은 무엇입니까?

  1. 항상 피하십시오 printf(). 신호 처리기에서 사용하지 마십시오 .

  2. POSIX 준수 시스템에 적어도, 당신이 사용할 수있는 write(STDOUT_FILENO, ...)대신에 printf(). 그러나 형식화가 쉽지 않을 수 있습니다. 쓰기 또는 비동기 안전 함수를 사용하여 신호 처리기에서 int 인쇄


1
Alk Always avoid it.는 의미합니까? 피 printf()하시겠습니까?
Grijesh Chauhan 2013-06-03

2
@GrijeshChauhan : 예, OP가 printf()시그널 핸들러에서 사용을 피해야 할 때를 묻기 때문 입니다.
ALK

2포인트에 대해 Alk +1 , 신호 처리기에서 사용을 피하는 방법 을 묻는 OP를 확인하십시오 printf().
Grijesh Chauhan 2013-06-03

7

디버깅 목적으로, 실제로는 async-signal-safe목록의 함수 만 호출하는지 확인 하고 신호 컨텍스트 내에서 호출되는 안전하지 않은 함수마다 경고 메시지를 출력 하는 도구를 작성했습니다 . 신호 컨텍스트에서 비동기 안전하지 않은 함수를 호출하려는 문제를 해결하지는 못하지만 적어도 실수로 수행 한 경우를 찾는 데 도움이됩니다.

소스 코드는 GitHub에 있습니다 . 오버로딩 signal/sigaction한 다음 일시적 PLT으로 안전하지 않은 기능 의 항목을 가로 채서 작동합니다. 이로 인해 안전하지 않은 함수에 대한 호출이 래퍼로 리디렉션됩니다.



1

자체 비동기 신호 안전 구현 snprintf("%d및 사용write

내가 생각했던 것만 큼 나쁘지 않습니다 . C에서 int를 문자열로 변환하는 방법? 몇 가지 구현이 있습니다.

신호 처리기가 액세스 할 수있는 흥미로운 데이터 유형은 두 가지뿐입니다.

  • sig_atomic_t 글로벌
  • int 신호 인수

이것은 기본적으로 모든 흥미로운 사용 사례를 다룹니다.

사실 그 strcpy또한 신호 안전 은 상황을 더욱 개선합니다.

아래 POSIX 프로그램은 지금까지 SIGINT를 수신 한 횟수를 표준 출력으로 인쇄합니다. Ctrl + C 하고, 및 신호 ID로 .

다음을 사용하여 프로그램을 종료 할 수 있습니다. Ctrl + \(SIGQUIT)로 .

main.c :

#define _XOPEN_SOURCE 700
#include <assert.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

/* Calculate the minimal buffer size for a given type.
 *
 * Here we overestimate and reserve 8 chars per byte.
 *
 * With this size we could even print a binary string.
 *
 * - +1 for NULL terminator
 * - +1 for '-' sign
 *
 * A tight limit for base 10 can be found at:
 * /programming/8257714/how-to-convert-an-int-to-string-in-c/32871108#32871108
 *
 * TODO: get tight limits for all bases, possibly by looking into
 * glibc's atoi: /programming/190229/where-is-the-itoa-function-in-linux/52127877#52127877
 */
#define ITOA_SAFE_STRLEN(type) sizeof(type) * CHAR_BIT + 2

/* async-signal-safe implementation of integer to string conversion.
 *
 * Null terminates the output string.
 *
 * The input buffer size must be large enough to contain the output,
 * the caller must calculate it properly.
 *
 * @param[out] value  Input integer value to convert.
 * @param[out] result Buffer to output to.
 * @param[in]  base   Base to convert to.
 * @return     Pointer to the end of the written string.
 */
char *itoa_safe(intmax_t value, char *result, int base) {
    intmax_t tmp_value;
    char *ptr, *ptr2, tmp_char;
    if (base < 2 || base > 36) {
        return NULL;
    }

    ptr = result;
    do {
        tmp_value = value;
        value /= base;
        *ptr++ = "ZYXWVUTSRQPONMLKJIHGFEDCBA9876543210123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[35 + (tmp_value - value * base)];
    } while (value);
    if (tmp_value < 0)
        *ptr++ = '-';
    ptr2 = result;
    result = ptr;
    *ptr-- = '\0';
    while (ptr2 < ptr) {
        tmp_char = *ptr;
        *ptr--= *ptr2;
        *ptr2++ = tmp_char;
    }
    return result;
}

volatile sig_atomic_t global = 0;

void signal_handler(int sig) {
    char key_str[] = "count, sigid: ";
    /* This is exact:
     * - the null after the first int will contain the space
     * - the null after the second int will contain the newline
     */
    char buf[2 * ITOA_SAFE_STRLEN(sig_atomic_t) + sizeof(key_str)];
    enum { base = 10 };
    char *end;
    end = buf;
    strcpy(end, key_str);
    end += sizeof(key_str);
    end = itoa_safe(global, end, base);
    *end++ = ' ';
    end = itoa_safe(sig, end, base);
    *end++ = '\n';
    write(STDOUT_FILENO, buf, end - buf);
    global += 1;
    signal(sig, signal_handler);
}

int main(int argc, char **argv) {
    /* Unit test itoa_safe. */
    {
        typedef struct {
            intmax_t n;
            int base;
            char out[1024];
        } InOut;
        char result[1024];
        size_t i;
        InOut io;
        InOut ios[] = {
            /* Base 10. */
            {0, 10, "0"},
            {1, 10, "1"},
            {9, 10, "9"},
            {10, 10, "10"},
            {100, 10, "100"},
            {-1, 10, "-1"},
            {-9, 10, "-9"},
            {-10, 10, "-10"},
            {-100, 10, "-100"},

            /* Base 2. */
            {0, 2, "0"},
            {1, 2, "1"},
            {10, 2, "1010"},
            {100, 2, "1100100"},
            {-1, 2, "-1"},
            {-100, 2, "-1100100"},

            /* Base 35. */
            {0, 35, "0"},
            {1, 35, "1"},
            {34, 35, "Y"},
            {35, 35, "10"},
            {100, 35, "2U"},
            {-1, 35, "-1"},
            {-34, 35, "-Y"},
            {-35, 35, "-10"},
            {-100, 35, "-2U"},
        };
        for (i = 0; i < sizeof(ios)/sizeof(ios[0]); ++i) {
            io = ios[i];
            itoa_safe(io.n, result, io.base);
            if (strcmp(result, io.out)) {
                printf("%ju %d %s\n", io.n, io.base, io.out);
                assert(0);
            }
        }
    }

    /* Handle the signals. */
    if (argc > 1 && !strcmp(argv[1], "1")) {
        signal(SIGINT, signal_handler);
        while(1);
    }

    return EXIT_SUCCESS;
}

컴파일 및 실행 :

gcc -std=c99 -Wall -Wextra -o main main.c
./main 1

Ctrl + C를 15 번 누르면 터미널에 다음이 표시됩니다.

^Ccount, sigid: 0 2
^Ccount, sigid: 1 2
^Ccount, sigid: 2 2
^Ccount, sigid: 3 2
^Ccount, sigid: 4 2
^Ccount, sigid: 5 2
^Ccount, sigid: 6 2
^Ccount, sigid: 7 2
^Ccount, sigid: 8 2
^Ccount, sigid: 9 2
^Ccount, sigid: 10 2
^Ccount, sigid: 11 2
^Ccount, sigid: 12 2
^Ccount, sigid: 13 2
^Ccount, sigid: 14 2

2신호 번호는 어디에 있습니까?SIGINT ?

Ubuntu 18.04에서 테스트되었습니다. GitHub 업스트림 .


0

선택 루프 가 있는 프로그램에서 특히 유용한 한 가지 기술은 신호 수신시 파이프에 단일 바이트를 기록한 다음 선택 루프에서 신호를 처리하는 것입니다. 다음 과 같은 내용 (간결성을 위해 오류 처리 및 기타 세부 정보 생략) :

static int sigPipe[2];

static void gotSig ( int num ) { write(sigPipe[1], "!", 1); }

int main ( void ) {
    pipe(sigPipe);
    /* use sigaction to point signal(s) at gotSig() */

    FD_SET(sigPipe[0], &readFDs);

    for (;;) {
        n = select(nFDs, &readFDs, ...);
        if (FD_ISSET(sigPipe[0], &readFDs)) {
            read(sigPipe[0], ch, 1);
            /* do something about the signal here */
        }
        /* ... the rest of your select loop */
    }
}

어떤 신호 인지 신경 쓰면 파이프 아래의 바이트가 신호 번호가 될 수 있습니다.


-1

pthread 라이브러리를 사용하는 경우 신호 처리기에서 printf를 사용할 수 있습니다. unix / posix는 printf가 스레드에 대해 원자임을 지정합니다. cf Dave Butenhof는 여기에 답장합니다. https://groups.google.com/forum/#!topic/comp.programming.threads/1-bU71nYgqw 더 명확한 그림을 얻으려면 printf 출력의 경우 GUI에 의해 생성 된 의사 tty가 아닌 콘솔에서 애플리케이션을 실행해야합니다 (리눅스에서는 ctl + alt + f1을 사용하여 콘솔 1을 시작).


3
시그널 핸들러는 별도의 스레드에서 실행되지 않고 시그널 인터럽트가 발생했을 때 실행 중이던 스레드의 컨텍스트에서 실행됩니다. 이 대답은 완전히 틀 렸습니다.
itaych
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.