2011-01-28 1 views
6

"코드 목록 1"과 "코드 목록 2"간에 차이가 있습니까? 왜냐하면 코드 목록 1에서 자식 프로세스는 SIGTERM 신호를 잡아 당겨 멋지게 ​​종료 할 수 있기 때문입니다. 그러나 코드 목록 2는 SIGTERM 신호에서 갑자기 종료됩니다. 나는 리눅스와 C포크 후 호출 신호

코드 목록 1

if (signal(SIGTERM, stopChild) == SIG_ERR) { 
    printf("Could not attach signal handler\n"); 
    return EXIT_FAILURE; 
} 
pid = fork(); 

코드 목록이

pid = fork(); 
if (signal(SIGTERM, stopChild) == SIG_ERR) { 
    printf("Could not attach signal handler\n"); 
    return EXIT_FAILURE; 
} 

을 사용하고

이상한 부분은 코드 목록 2에 그 입니다, 자식 프로세스와 부모 프로세스 모두 SIGTERM에 대한 신호 처리기를 설정합니다.. 그래서, 이것은 일하기로되어 있습니다. 그렇지 않니?

+0

방금 ​​테스트 해 보았지만 제대로 작동합니다. 두 경우 모두 stopChild() 호출을 통해 두 프로세스가 정상적으로 종료됩니다. –

+0

스레드 내에서 fork()를 호출 할 가능성이 있습니까? –

+2

동작을 보여주는 전체 예제 프로그램을 제공 할 수 있습니까? – wich

답변

5

부모로부터 SIGTERM을 보내는 경우 최종 결과는 프로세스가 예약 된 순서에 따라 다릅니다. 아이가 처음 예정 도착하면

, 모든 작동합니다

           +---------------+ 
               | pid = fork(); | 
               +-------+-------+ 
        parent        |        child 
          +-----------------------------+-----------------------------+ 
          |               | 
+-------------------------+--------------------------+        | 
| if (signal(SIGTERM, stopChild) == SIG_ERR) {  |        | 
|  printf("Could not attach signal handler\n"); |        | 
|  return EXIT_FAILURE;       |        | 
| }             |        | 
+-------------------------+--------------------------+        | 
          |               | 
          |               | 
          |               | 
      +-------------+-------------+            | 
      | if (pid > 0) {   |            | 
      |  kill(pid, SIGTERM); |            | 
      | }       |            | 
      +-------------+-------------+            | 
          |               | 
          .               . 
          .               . 
          .               . 
          |               | 
          |         +-------------------------+--------------------------+ 
          |         | if (signal(SIGTERM, stopChild) == SIG_ERR) {  | 
          |         |  printf("Could not attach signal handler\n"); | 
          |         |  return EXIT_FAILURE;       | 
          |         | }             | 
          |         +-------------------------+--------------------------+ 
          |               | 
          |               | 
          |               | 

이 :

           +---------------+ 
               | pid = fork(); | 
               +-------+-------+ 
        parent        |        child 
          +-----------------------------+-----------------------------+ 
          |               | 
          |         +-------------------------+--------------------------+ 
          |         | if (signal(SIGTERM, stopChild) == SIG_ERR) {  | 
          |         |  printf("Could not attach signal handler\n"); | 
          |         |  return EXIT_FAILURE;       | 
          |         | }             | 
          |         +-------------------------+--------------------------+ 
          |               | 
          .               . 
          .               . 
          .               . 
          |               | 
+-------------------------+--------------------------+        | 
| if (signal(SIGTERM, stopChild) == SIG_ERR) {  |        | 
|  printf("Could not attach signal handler\n"); |        | 
|  return EXIT_FAILURE;       |        | 
| }             |        | 
+-------------------------+--------------------------+        | 
          |               | 
          |               | 
          |               | 
      +-------------+-------------+            | 
      | if (pid > 0) {   |            | 
      |  kill(pid, SIGTERM); |            | 
      | }       |            | 
      +-------------+-------------+            | 
          |               | 
          |               | 
          |               | 

그러나 괄호가 먼저 예정 도착하면, 아이가 셋업 시간을 신호 처리기가 없었어요 수

경쟁 조건이라고합니다. 최종 결과는 누가 먼저 실행해야하는지에 달려 있기 때문입니다.

+0

부모 프로세스가 신호를 보내기 전에 10 초간 수면을 취했기 때문에 이것이 문제라고 생각하지 않습니다. 어쨌든이 문제를 재현 할 수 없으므로이 대답을 지금 받아 들일 것입니다. – Sabya

0

음, 남자 포크에 따라 :

포크()은 fork1() 및 forkall() 함수는 새로운 프로세스를 만들 수 있습니다. 새 프로세스 (하위 프로세스)의 주소 공간은 호출 프로세스 (상위 프로세스)의 주소 공간과 정확히 일치합니다. 자식 프로세스는 부모 프로세스에서 다음과 같은 속성을 상속

... 첫 번째 예

O 신호 처리 설정 (즉, SIG_DFL, SIG_IGN, SIG_HOLD 함수 주소)

시그널 핸들러는 부모 컨텍스트에서 분기 된 자식으로 복사됩니다. 하지만 두 번째 예제 설정에서 자식의 신호 처리기가 실패하는 이유를 설명 할 수 없습니다.

+1

그러나 두 번째 예제에서는 두 프로세스 모두에서 signal()이 호출됩니다. 그래서 그것은 자식 프로세스를위한 시그널 핸들러를 설정해야한다. –

+0

@ Sergey, 정확히 그게 내 의심이야! – Sabya

+0

POSIX는 부모에 대한 모든 _pending_ 신호가 자식으로 전송되지 않는다고 말하면, 0으로 초기화 된 신호 세트로 시작합니다. 그러나 자식은 처리기를 상속해야합니다. 그러나 pthread_atfork()가없는 스레드에서 fork()를 호출하면 OP가 겪고있는 것을 설명 할 수 있습니다. –

3

먼저 signal()은 더 이상 사용되지 않으므로 sigaction()을 사용하는 것이 더 좋습니다. 나는 많은 것들이 그것을 사용하기 때문에 fork()가 완전히 사라질 위험이 있다고 생각하지 않지만, sigaction()은 훨씬 좋은 인터페이스를 제공한다.

발생하는 문제는 일반적으로 스레드 내에서 fork()를 호출하면 발생합니다. POSIX이 specifically 주소 :

프로세스가 단일 스레드로 생성해야한다. 다중 스레드 프로세스가 fork()를 호출하면 새로운 프로세스 은 호출 의 복제본과 전체 주소 공간 (가능하면 뮤텍스 및 기타 자원의 상태 포함)을 포함합니다. 따라서 오류를 방지하기 위해 자식 프로세스는 비동기 신호 안전 작업 때까지 exec 함수 중 하나가 호출 될 때까지만 실행할 수 있습니다. [THR] pthread_atfork() 함수를 사용하여 fork() 호출에서 응용 프로그램의 불변 조건을 유지하기 위해 포크 핸들러를 으로 설정할 수 있습니다.

신호 처리기 및 pthread_atfork 명령 등록한 포크 중 핸들러()에서 애플리케이션 호출 포크() 는 비동기 신호 안전하지 않은 것 함수를 호출

는 동작이 정의되지 않는다.

이, 당신은 전화 스레드 당신의 핸들러를 포함하지 않는 주소 공간의 복사본을 상속 오히려 부모의 전체 주소 공간의 복사본을 상속보다 의미한다. 스레드 내에서 fork()를 호출하는 것은 실제로 (아마도 무의식적으로) 생각할 수도 있습니다.

자식 프로세스가 부모 주소 공간의 카피 본을 가져옵니다. 신호와의 유일한 차이는 보류중인 신호이며, 신호 세트가 0으로 초기화되기 때문에 수신하지 못합니다. 하지만 네, 핸들러 사본을 얻습니다.

+0

포크가 실제로 성공적이기 전에 신호를 호출하기 때문에 신호 처리는 포크가 깨진 후 아이를 복사하고, 아이가 부르는 시그널이 작동하지 않을 때 호출합니다. – wich

+0

@wich - 그래서 fork()가 스레드에서 호출되고 있다는 것이 매우 의심 스럽습니다. –