2009-11-04 2 views
5

Meine User-Space-Anwendung blockiert manchmal nach dem Empfang eines EINTR-Signals irgendwie.Umgang mit EINTR (unterbrochener Systemanruf)

Was ich mit strace aufgezeichnet:

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 

Kann ich das EINTR Signal fangen und wie kann ich besorgt Anrufe wie Schreib wiederholen, lesen oder wählen Sie? Wie kann ich feststellen, wo diese EINTR aufgetreten ist, auch wenn ich Bibliotheken von Drittanbietern verwendet, die mit Systemaufrufen arbeiten?

Warum ist meine App nach Erhalt eines EINTR vollständig blockiert (siehe strace dump: Ich habe eine SIGUSR1 gesendet, die normalerweise behandelt werden sollte)? Und warum bringt Futex() ERESTARTSYS zurück in den Benutzerbereich?

dank

+3

EINTR ist kein Signal, sondern eine Fehlernummer zurückgegeben auf einen Systemaufruf durch ein Signal unterbrochen. –

+0

Verwenden Sie 'gstack' oder' gdb', um einen Stack-Trace zu erhalten, um herauszufinden, wo das Programm gerade hängt. – mark4o

Antwort

17

Der Code, der Schreib- (oder andere Blockierungsoperationen) ruft hat bewusst EINTR sein. Wenn während einer Blockierungsoperation ein Signal auftritt, führt die Operation entweder (a) eine teilweise Vervollständigung aus oder (b) gibt einen Fehler zurück, unternimmt nichts und setzt errno auf EINTR.

Also, für eine Schreiboperation Alles-oder-fail, die nach Unterbrechungen wiederholt, dann würden Sie so etwas tun:

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 

Oder für etwas ein bisschen besser benommen, die EINTR wiederholt, schreibt so viel wie er kann, und berichtet, wie viel bei einem Fehler geschrieben (so kann der Anrufer entscheiden, ob und wie Teil schreibt fortzusetzen, die aus einem bestimmten Grunde als Unterbrechung andere nicht durch das Signal):

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 hat einen nicht-Standard TEMP_FAILURE_RETRY Makro, das von Interesse sein könnte, obwohl ich kann nie die Dokumente dafür finden, wenn ich sie will. Einschließlich jetzt.

+1

Danke, ich habe die Docu zum Makro gefunden http://www.gnu.org/s/libc/manual/html_node/Interruped-Primitives.html Kennen Sie etwas über die gesperrte Funktion futex()? – Maus

+1

Weiß nicht über die ERESTARTSYS. Ich denke, es handelt sich um ein internes Implementierungsdetail - Sie sehen es in der Ablaufverfolgung, sollten es aber nie wieder zum Benutzercode zurückkehren sehen, da der Systemcode im Benutzermodus den Aufruf, der es zurückgegeben hat, entweder wiederholt oder in EINTR konvertiert . Aber ich könnte mich damit irren, ich habe mich am Samstag rasiert und habe deshalb keine vollständigen Linux-Geek-Credentials ;-) –

+2

futex ist ein Linux-spezifisches "light-wait" -Sperrframework zum Aufbau höherer Level wie Semaphore und Mutex. Siehe futex (7). –