2017-02-12 4 views
0

Ich habe Zsh-Modul geschrieben. Dort habe ich eine eingebaute Funktion, die dem Zsh Befehl zugeordnet ist. Diese Funktion dupliziert seine stdin Dateideskriptor:dup (fileno (stdin)) und dann spawnen von 32 Threads -> I/O-Fehler

/* Duplicate standard input */ 
oconf->stream = fdopen(dup(fileno(stdin)), "r"); 

Dann wird ein Faden wird hervorgebracht, die oconf Struktur erhält. In diesem Thread, ich tun:

errno = 0; 

/* Read e.g. 5 characters, putting them after previous portion */ 
int count = fread(buf + index, 1, read_size, oconf->stream); 
/* Ensure that our whole data is a string - null terminated */ 
buf[ index + count ] = '\0'; 

if (errno) { 
    fprintf(oconf->err, "Read error (descriptor: %d): %s\n", fileno(oconf->stream), strerror(errno) > 
} 

Wenn I Laichen 32 Threads in zsh:

for ((i=1; i<=32; i ++)); do 
    ls -R /Users/myuser/Documents | mybuiltin -A myhash_$i $i 
done 

Dann 2-3 Fäden haben die I/O-Fehler aus dem obigen fprintf() berichtet, zum Beispiel:

Lesefehler (Deskriptor: 7): Eingabe/Ausgabe-Fehler

Lesefehler (descriptor: 5): Ungeeignete ioctl für Geräte

Lesefehler (Deskriptor: 14): Ungeeignete ioctl für Geräte

Debugger sagt, dass diese Fäden, nach mehrfachen (5-20) fread() von Wiederholungen in Kernels __read_nocancel() blockiert werden. Mit dem Dateideskriptor läuft also etwas wirklich Schlechteres.

Ansonsten funktioniert das. Die Pipe übergibt Daten korrekt von ls -R, sie wird vom benutzerdefinierten Built-in gelesen. Wo ist die Gefahr? Wie kommt es, dass dup() im Haupt-Thread führt zu etwas unlesbar zu fread() führt? Ich könnte Zweifel haben, ob ich dup() in sekundären Thread tun würde. Aber ich behalte das nur an einem sicheren Ort - Haupt-Thread, um dann bereit FILE * Stream zu sekundären Thread zu übergeben. Auch mit POSIX open(), read() und close() versucht, das Ergebnis ist das gleiche.

Antwort

0

Sie testen errno falsch. Sie sollten nur errno überprüfen, wenn eine Funktion, die es setzt, einen Fehler gemeldet hat. Keine Funktion in der Standard C- oder POSIX-Bibliothek setzt errno jemals auf Null. Und Funktionen können errno auf nicht Null setzen, ohne einen Fehler zu melden. Unter Solaris war es früher (und wahrscheinlich immer noch) der Fall, dass nach Schreibvorgängen errno == ENOTTY der Dateistream kein Terminal war (z. B. umgeleitet zu einer Datei oder einer Pipe). Es gab kein Problem; Das Ausgabegerät war kein Terminal, daher ist ein Terminal-Only-Vorgang fehlgeschlagen. Setzen Sie errno auf ENOTTY.

Sie haben zur Zeit:

/* Read e.g. 5 characters, putting them after previous portion */ 
int count = fread(buf + index, 1, read_size, oconf->stream); 
/* Ensure that our whole data is a string - null terminated */ 
buf[ index + count ] = '\0'; 

if (errno) { 
    fprintf(oconf->err, "Read error (descriptor: %d): %s\n", fileno(oconf->stream), strerror(errno)); 
} 

Sie brauchen etwas verwenden, eher wie:

int count = fread(buf + index, 1, read_size, oconf->stream); 
if (count == 0) 
{ 
    /* EOF or error — this might be a time to use feof() or ferror() */ 
    fprintf(oconf->err, "Read error (descriptor: %d): %s\n", fileno(oconf->stream), strerror(errno)); 
    …flow control?… 
} 
else 
    buf[index + count] = '\0'; 

Sie wahrscheinlich einige andere Flow-of-Control-Details (Rückkehr oder brechen oder Set-Flag) müssen in der EOF und der Fehlerpfad; Aus dem zitierten Fragment ist nicht klar, was angemessen sein könnte.