2012-05-23 15 views
8

W poniższym programie, jeśli odkomentuję linię , mój program zakończy się, gdy uderzę C-c, ten sam program się nie zakończy Jeśli nie skomentuję tej linii. Ktoś wie, w jaki sposób zmiana sygnału wpływa na kodowanie _XOPEN_SOURCE? Jestem na Linuksie z gcc (4.6.3) i glibc (2.15).XOPEN_SOURCE i obsługa sygnałów

/* #define _XOPEN_SOURCE 700 */ 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <signal.h> 

typedef void (*sighandler_t)(int); 

void handle_signal(int signo) 
{ 
    printf("\n[MY_SHELL] "); 
    fflush(stdout); 
} 

int main() 
{ 
    int c; 
    signal(SIGINT, SIG_IGN); 
    signal(SIGINT, handle_signal); 
    printf("[MY_SHELL] "); 
    while ((c = getchar()) != EOF) { 
     if (c == '\n') 
      printf("[MY_SHELL] "); 
    } 
    printf("\n"); 
    return 0; 
} 
+0

Na marginesie: 'char c': no, no, no, no, no. 'EOF' musi być poza pasmem. Dlatego 'getchar' zwraca' int'. – Dave

+0

@Dave Masz rację. To był faktycznie przykład znaleziony w Internecie. Nie zauważyłem tego. Napraw to teraz. – yasar

Odpowiedz

4

Problemem jest to, że funkcja signal() może mieć dwie różne formy zachowania podczas instalowania funkcję obsługi sygnału:

  • System V semantyki, gdzie procedura obsługi sygnału jest „one-shot” - że to, po wywołaniu funkcji obsługi sygnału, dyspozycja sygnału jest resetowana do SIG_DFL - a wywołania systemowe, które są przerywane przez sygnał, nie są ponownie uruchamiane; lub
  • BSD semantyka, gdzie procedura obsługi sygnału jest nie zresetowane podczas pożarów sygnał, sygnał jest blokowany podczas obsługi sygnału jest wykonywany, a najbardziej przerwane wywołania systemowe są automatycznie ponownie uruchomiony.

W systemie Linux z glibc uzyskuje się semantykę BSD, jeśli zdefiniowano _BSD_SOURCE, a semantykę System V, jeśli tak nie jest. Makro _BSD_SOURCE jest zdefiniowane domyślnie, ale ta domyślna definicja jest pomijana, jeśli zdefiniujesz _XOPEN_SOURCE (lub kilka innych makr, takich jak _POSIX_SOURCE i _SVID_SOURCE).

pod kontrolą systemu V semantyka, czy funkcja systemowa read() bazowego getchar() przerywa SIGINT następnie getchar() powróci EOFerrno z zestawem do EINTR (spowoduje to program do wyjść normalnie). Ponadto po pierwszym SIGINT ustawienie tego sygnału zostanie zresetowane do wartości domyślnej, a domyślną akcją dla SIGINT jest zakończenie procesu (więc nawet jeśli twój program przeżył pierwszą SIGINT, druga spowodowałaby nieprawidłowe wyjście).

Rozwiązanie polega na tym, aby w ogóle nie używać funkcji signal() do instalowania funkcji obsługi sygnałów; zamiast tego powinieneś używać sigaction(), która jest przenośna - wszędzie daje tę samą semantykę. Z sa_flags ustawionym na SA_RESTART, sigaction() da to semantykę BSD, która jest tym, czego potrzebujesz.

+0

Należy jednak zauważyć, że w każdym przypadku program nie zakończy się, gdy C-c zostanie naciśnięty po raz pierwszy (z już ustawionym programem obsługi). –

+0

To nie jest to, co się tutaj dzieje. W semantyce '_XOPEN_SOURCE 700' brakuje' SA_RESTART', powodując 'EINTR' i zwracając' EOF' w getchar, wychodząc z programu. Zobacz moją odpowiedź. – Dave

+0

@Dave: Masz całkowitą rację, zaktualizowałem tę odpowiedź za pomocą informacji o ponownym uruchomieniu syscall. – caf

1

Te subtelne różnice behavorial to dlaczego sigprocmask() jest zwykle korzystne signal() wersji z _XOPEN_SOURCE 700 określonych połączeń (jak pokazano przez strace)

rt_sigaction(SIGINT, {SIG_IGN, [], SA_INTERRUPT|SA_NODEFER|SA_RESETHAND}, {SIG_DFL, [], 0}, 8) = 0 
rt_sigaction(SIGINT, {0x80484dc, [], SA_INTERRUPT|SA_NODEFER|SA_RESETHAND}, {SIG_IGN, [], SA_INTERRUPT|SA_NODEFER|SA_RESETHAND}, 8) = 0 

Podczas gdy zakomentowanych jeden nazywa:

rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART}, {SIG_DFL, [], 0}, 8) = 0 
rt_sigaction(SIGINT, {0x80484dc, [INT], SA_RESTART}, {SIG_IGN, [INT], SA_RESTART}, 8) = 0 

Ważną dodatkową flagą, której nie ma _XOPEN_SOURCE, jest SA_RESTART, która pozwala na kontynuowanie wywołania systemowego read, tak jakby brakowało sygnału. Bez tego wywołanie systemowe wskazuje na błąd, getchar() zwraca -1 (ale wskazuje awarię, zamiast prawdziwego EOF), a twój program się kończy.