2009-11-04 10 views
5

Moja aplikacja przestrzeni użytkownika czasami blokuje się po otrzymaniu sygnału EINTR, w jakiś sposób.Obsługa EINTR (przerwane wywołanie systemowe)

Co nagrałem z strace:

time(NULL)        = 1257343042 
time(NULL)        = 1257343042 
rt_sigreturn(0xbff07be4)    = -1 EINTR (Interrupted system call) 
--- SIGALRM (Alarm clock) @ 0 (0) --- 
time(NULL)        = 1257343042 
futex(0xb7cea80c, 0x80 /* FUTEX_??? */, 2) = ? ERESTARTSYS (To be restarted) 
--- SIGUSR1 (User defined signal 1) @ 0 (0) --- 
sigreturn()        = ? (mask now [ALRM]) 
futex(0xb7cea80c, 0x80 /* FUTEX_??? */, 2) = ? ERESTARTSYS (To be restarted) 
--- SIGWINCH (Window changed) @ 0 (0) --- 
futex(0xb7cea80c, 0x80 /* FUTEX_??? */, 2) = ? ERESTARTSYS (To be restarted) 
--- SIGTERM (Terminated) @ 0 (0) --- 
time(NULL)        = 1257343443 
time(NULL)        = 1257343443 
futex(0xb7cea80c, 0x80 /* FUTEX_??? */, 2) = ? ERESTARTSYS (To be restarted) 
--- SIGWINCH (Window changed) @ 0 (0) --- 
futex(0xb7cea80c, 0x80 /* FUTEX_??? */, 2 

mogę złapać sygnał Eintr i jak mogę powtórzyć dotyczyło połączeń, takich jak pisać, czytać lub wybrać? Jak ustalić, GDZIE wystąpił ten błąd EINTR, nawet jeśli korzystałem z bibliotek innych firm pracujących z wywołaniami systemowymi?

Dlaczego moja aplikacja jest całkowicie zablokowana po otrzymaniu EINTR (zobacz strace dump: wysłałem SIGUSR1, który normalnie powinien być obsługiwany)? I dlaczego futext() zwraca ERESTARTSYS do przestrzeni użytkownika?

dzięki

+3

EINTR nie jest to sygnał, ale numer błędu zwracana przy wywołania systemowego przerwany przez sygnał. –

+0

Użyj 'gstack' lub' gdb', aby uzyskać ślad stosu, aby dowiedzieć się, gdzie aktualnie utknął program. – mark4o

Odpowiedz

17

Kod, który wywołuje napisać (lub innych operacji blokowania) musi być świadomy EINTR. Jeśli wystąpi sygnał podczas operacji blokowania, wówczas operacja albo (a) zwróci częściowe zakończenie, albo (b) zwróci błąd, nie wykona niczego i ustawi errno na EINTR.

Tak więc, na wszystko-albo-nie operacji zapisu, które po ponownych prób przerw, można zrobić coś takiego:

while(size > 0) { 
    int written = write(filedes, buf, size); 
    if (written == -1) { 
     if (errno == EINTR) continue; 
     return -1; 
    } 
    buf += written; 
    size -= written; 
} 
return 0; // success 

Albo czegoś nieco lepiej wychowane, który RETRIES EINTR, pisze tyle jak to możliwe, a raporty, ile jest napisane na niepowodzenie (tak dzwoniący może zdecydować, czy i jak kontynuować częściowe zapisy, które zawodzą z powodów innych niż przerwanie przez sygnał):

int total = 0; 
while(size > 0) { 
    int written = write(filedes, buf, size); 
    if (written == -1) { 
     if (errno == EINTR) continue; 
     return (total == 0) ? -1 : total; 
    } 
    buf += written; 
    total += written; 
    size -= written; 
} 
return total; // bytes written 

GNU ma niestandardową Makro TEMP_FAILURE_RETRY, które może być interesujące, chociaż ja nigdy nie znajdę dokumentów na ten temat, kiedy ich chcę. W tym teraz.

+1

Dzięki, znalazłem Docu do makra na http://www.gnu.org/s/libc/manual/html_node/Interrupted-Primitives.html Czy wiesz coś o zamkniętej funkcji futex()? – Maus

+1

Nie wiem o ERESTARTSYS. Wydaje mi się, że jest to szczegół implementacji wewnętrznej - widzisz go w śledzeniu, ale nigdy nie widzisz, by zwracał kod użytkownika, ponieważ kod systemowy trybu użytkownika powinien albo ponawiać połączenie, które je zwróciło, albo konwertować na EINTR . Ale może się mylę, ostatnio goliłem się w sobotę i dlatego nie mam nic takiego jak pełne referencje geeków linuxowych ;-) –

+2

futex to specyficzna dla Linuksa struktura blokowania "light-wait" do budowania rzeczy na wyższym poziomie, takich jak semafor i muteks. Zobacz futex (7). –

Powiązane problemy