2011-12-06 3 views
0

나는 많은 양의 아이를 생성하고 오랜 시간 동안 실행되는 프로그램을 가지고 있습니다. 이 프로그램에는 존재하지 않는 프로세스를 얻기위한 SIGCHLD 핸들러가 들어 있습니다. 때때로이 프로그램이 정지합니다. 나는 pstack이 교착 상태 시나리오를 나타내고 있다고 믿는다. 이것이이 산출물의 적절한 해석인가?교착 상태 시나리오에 대한 지침 찾기

10533: ./asyncsignalhandler 
ff3954e4 lwp_park (0, 0, 0) 
ff391bbc slow_lock (ff341688, ff350000, 0, 0, 0, 0) + 58 
ff2c45c8 localtime_r (ffbfe7a0, 0, 0, 0, 0, 0) + 24 
ff2ba39c __posix_ctime_r (ffbfe7a0, ffbfe80e, ffbfe7a0, 0, 0, 0) + c 
00010bd8 gettimestamp (ffbfe80e, ffbfe828, 40, 0, 0, 0) + 18 
00010c50 sig_chld (12, 0, ffbfe9f0, 0, 0, 0) + 30 
ff3956fc __sighndlr (12, 0, ffbfe9f0, 10c20, 0, 0) + c 
ff38f354 call_user_handler (12, 0, ffbfe9f0, 0, 0, 0) + 234 
ff38f504 sigacthandler (12, 0, ffbfe9f0, 0, 0, 0) + 64 
--- called from signal handler with signal 18 (SIGCLD) --- 
ff391c14 pthread_mutex_lock (20fc8, 0, 0, 0, 0, 0) + 48 
ff2bcdec getenv (ff32a9ac, 770d0, 0, 0, 0, 0) + 1c 
ff2c6f40 getsystemTZ (0, 79268, 0, 0, 0, 0) + 14 
ff2c4da8 ltzset_u (4ede65ba, 0, 0, 0, 0, 0) + 14 
ff2c45d0 localtime_r (ffbff378, 0, 0, 0, 0, 0) + 2c 
ff2ba39c __posix_ctime_r (ffbff378, ffbff402, ffbff378, ff33e000, 0, 0) + c 
00010bd8 gettimestamp (ffbff402, ffbff402, 2925, 29a7, 79c38, 10b54) + 18 
00010ae0 main  (1, ffbff4ac, ffbff4b4, 20c00, 0, 0) + 190 
00010928 _start (0, 0, 0, 0, 0, 0) + 108 

저는 C 코더를 좋아하지 않으며 언어의 미묘한 차이에 익숙하지 않습니다. 나는 특히 프로그램에서 ctime (_r)의 re-entrant 버전을 사용하고있다. 왜 여전히 교착 상태에 빠졌습니까?

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#include <time.h> 

// import pid_t type 
#include <sys/types.h> 

// import _exit function 
#include <unistd.h> 

// import WNOHANG definition 
#include <sys/wait.h> 

// import errno variable 
#include <errno.h> 

// header for signal functions 
#include <signal.h> 

// function prototypes 
void sig_chld(int); 
char * gettimestamp(char *); 

// begin 
int main(int argc, char **argv) 
{ 
    time_t sleepstart; 
    time_t sleepcheck; 
    pid_t childpid; 
    int i; 
    unsigned int sleeptime; 
    char sleepcommand[20]; 
    char ctime_buf[26]; 

    struct sigaction act; 

    /* set stdout to line buffered for logging purposes */ 
    setvbuf(stdout, NULL, _IOLBF, BUFSIZ); 

    /* Assign sig_chld as our SIGCHLD handler */ 
    act.sa_handler = sig_chld; 

    /* We don't want to block any other signals */ 
    sigemptyset(&act.sa_mask); 

    /* 
    * We're only interested in children that have terminated, not ones 
    * which have been stopped (eg user pressing control-Z at terminal) 
    */ 
    act.sa_flags = SA_NOCLDSTOP; 

    /* Make these values effective. */ 
    if (sigaction(SIGCHLD, &act, NULL) < 0) 
    { 
     printf("sigaction failed\n"); 
     return 1; 
    } 

    while (1) { 
     for (i = 0; i < 20; i++) { 
     /* fork/exec child program        */ 
     childpid = fork(); 
     if (childpid == 0) // child 
     { 
      //sleeptime = 30 + i; 
      sprintf(sleepcommand, "sleep %d", i); 

      printf("\t[%s][%d] Executing /bin/sh -c %s\n", gettimestamp(ctime_buf), getpid(), sleepcommand); 

      execl("/bin/sh", "/bin/sh", "-c", sleepcommand, NULL); 

      // only executed if exec fails 
      printf("[%s][%d] Error executing program, errno: %d\n", gettimestamp(ctime_buf), getpid(), errno); 
      _exit(1); 
     } 
     else if (childpid < 0) // error 
     { 
      printf("[%s][%d] Error forking, errno: %d\n", gettimestamp(ctime_buf), getpid(), errno); 
     } 
     else // parent 
     { 
      printf("[%s][%d] Spawned child, pid: %d\n", gettimestamp(ctime_buf), getpid(), childpid); 
     } 
     } 

     // sleep is interrupted by SIGCHLD, so we can't simply sleep(5) 
     printf("[%s][%d] Sleeping for 5 seconds\n", gettimestamp(ctime_buf), getpid()); 
     time(&sleepstart); 
     while (1) { 
     time(&sleepcheck); 
     if (difftime(sleepcheck, sleepstart) < 5) { 
      sleep(1); 
     } else { 
      break; 
     } 
     } 
    } 


    return(0); 
} 

char * gettimestamp(char *ctime_buf) 
{ 
    time_t now; 

    time(&now); 

    // format the timestamp and chomp the newline 
    ctime_r(&now, ctime_buf); 
    ctime_buf[strlen(ctime_buf) - 1] = '\0'; 

    return ctime_buf; 
} 

/* 
* The signal handler function -- only gets called when a SIGCHLD 
* is received, ie when a child terminates. 
*/ 
void sig_chld(int signo) 
{ 
    pid_t childpid; 
    int childexitstatus; 
    char ctime_buf[26]; 

    while (1) { 
     childpid = waitpid(-1, &childexitstatus, WNOHANG); 
     if (childpid > 0) 
     printf("[%s][%d] Reaped child, pid: %d, exitstatus: %d\n", gettimestamp(ctime_buf), getpid(), childpid, WEXITSTATUS(childexitstatus)); 
     else 
     return; 
    } 
} 

저는 Solaris 9 환경에서 실행됩니다.

cc -o asyncsignalhandler asyncsignalhandler.c -mt -D_POSIX_PTHREAD_SEMANTICS 

프로그램에 결함이 있습니까 :이 프로그램은 다음과 같은 구문을 사용하여 C 5.3 패치 111679-15 2009/09/10 일 워크숍 6 업데이트 2를 컴파일? 신호 처리기에서 로깅 (타임 스탬프 포함)을 처리하는 더 좋은 방법이 있습니까?

+0

'printf'는 async-signal-safe가 아니므로 시그널 핸들러 내부에서 사용해서는 안됩니다. 대신'fileno (stdout)'을 사용하여'write'를 사용하십시오. – Jason

+0

printf()가 문제가 될 수 있음을 인정 하겠지만 스택 추적에서 말하는 것이 아닙니다. 교착 상태의 원인으로 printf()에 어떻게 도달합니까? – user255205

답변

3

당신은 비동기 시그널에 안전 신호 처리기 내에서 (유닉스 사양의 section 2.4.3 참조)없는 기능을 요구하고있다 -이 경우에는, ctime_r()printf()는 (교착 상태로 인해 사용하는 잠금으로 일어나고있는 것으로 보인다 당신이 보여주는 stacktrace에 ctime_r()). 이러한 함수는 잠금을 취할 수 있으며 신호 핸들러가 언제든지 호출 될 수 있으므로 잠금이 이미 보유되어 교착 상태가 발생할 수 있습니다.

일반적으로 신호 처리기에서 주 스레드가 나중에 검사하도록해야합니다. 예를 들어, pipe() 생성 된 파일 설명자에 write() (비동기 신호 안전 함수)을 사용할 수 있으며 주 루프 (또는 다른 스레드)가 선택 루프를 수행하여 일부 데이터가 해당 파이프에 표시 될 때까지 대기하게 할 수 있습니다 .

스레드 안전비동기 신호 안전과 같지 않습니다. ctime_r은 스레드로부터 안전합니다. 스레드가 서로 밟지 않도록 잠금을 사용하며 정적 버퍼가 아니라 전달 된 버퍼를 사용합니다. 그러나 실행시 임의의 시점에서 재 호출 할 수 없으므로 비동기 신호에 안전하지 않습니다.

+0

localtime()을 호출하지 않습니다. 나는 ctime_r()을 호출하여 스택 추적 당 localtime_r()을 호출한다. – user255205

+0

아, 죄송합니다. 그러나'ctime_r()'도 async-signal-safe로 나열되지 않습니다. – bdonlan

+0

ctime_r()은 명시 적으로 재진입입니다. 나는 스레드 안전의 가장 높은 순서라고 생각 했습니까? 재진입하는 것이 비동기 신호에 안전하지 않습니까? – user255205