web-dev-qa-db-de.com

Blockieren von Linux im Vergleich zu nicht blockierendem seriellem Lesen

ich habe diesen Code zum Lesen von Serial in Linux, aber ich weiß nicht, was der Unterschied zwischen blockieren und nicht blockieren beim Lesen von Serial Port ist und welches ist in welcher Situation besser?

25

Der Code, den Sie erwähnen, ist IMO schlecht codiert und kommentiert. Dieser Code entspricht nicht den POSIX-Praktiken für die Portabilität, wie in Festlegen der Terminalmodi und Serielles Programmierhandbuch für POSIX-Betriebssysteme beschrieben. In diesem Code wird nicht erwähnt, dass der nicht-kanonische (auch als RAW bezeichnete) Modus verwendet wird, und die Terminologie "Blockieren" und "Nicht-Blockieren" wird zur Beschreibung der [~ # ~] vmin [~ # ~ verwendet ] und [~ # ~] vtime [~ # ~] Attribute.

(Der Autor dieses Codes gibt an, dass er älter ist als der POSIX-Standard und daher dessen Nichteinhaltung. Das ist verständlich, aber dann die Verwendung von altem Code zu veröffentlichen und zu befürworten, der möglicherweise nicht portierbar ist (dh in einer alternativen Situation wie erwartet funktioniert) ) ist fraglich.)

Die herkömmliche Definition eines "blockierenden" gegenüber eines "nicht blockierenden" Lesevorgangs basiert darauf, "wann" der Leseaufruf zu Ihrem Programm zurückkehrt (und die Ausführung mit der nächsten Anweisung fortsetzt) ​​und ob Daten im Lesepuffer Ihres Programms gespeichert werden. Ein blockierender Lesevorgang ist der Standardmodus, es sei denn, durch Öffnen der seriellen Schnittstelle mit der Option O_NONBLOCK oder O_NDELAY wird die Nichtblockierung angefordert.

Kanonischer Modus
Bei einem blockierenden kanonischen Lesevorgang Aufruf einer seriellen Schnittstelle wird immer eine Textzeile (auch als Datensatz bezeichnet) im bereitgestellten Puffer zurückgegeben (sofern kein Fehler aufgetreten ist). Der Leseaufruf blockiert (d. H. Setzt die Ausführung Ihres Programms aus), so lange es dauert, bis ein Zeilenbeendigungszeichen empfangen und verarbeitet wird.

Ein nicht blockierender kanonischer Leseaufruf einer seriellen Schnittstelle wird immer "sofort" zurückgegeben. Der Lesevorgang kann Daten zurückgeben oder nicht.
Wenn (seit dem letzten Leseaufruf) mindestens eine Textzeile empfangen und im Systempuffer gespeichert wurde, wird die älteste Zeile aus dem Systempuffer entfernt und in den Programmpuffer kopiert. Der Rückkehrcode gibt die Datenlänge an.
Wenn (seit dem letzten Leseaufruf) kein Zeilenbeendigungszeichen empfangen und verarbeitet wurde, ist keine (vollständige) Textzeile verfügbar. Das read () gibt einen EAGAIN-Fehler zurück (dh einen -1-Rückkehrcode und errno auf EAGAIN) gesetzt. Ihr Programm kann dann eine Berechnung durchführen oder E/A von einem anderen Gerät anfordern oder verzögern/schlafen. Entweder nach einer beliebigen Verzögerung oder durch Benachrichtigung durch poll () oder select () , Ihr Programm kann das read () wiederholen.

Ein Beispielprogramm, das den kanonischen Blockierungsmodus für Lesevorgänge verwendet, ist in diese Antwort enthalten.

Nicht-kanonischer Modus
Wenn die serielle Schnittstelle für den nicht-kanonischen Modus konfiguriert ist, wird der termios c_cc Array-Elemente [~ # ~] vmin [~ # ~] und [~ # ~] vtime [~ # ~] sollte verwendet werden, um das "Blockieren" zu steuern. Dies erfordert jedoch, dass der Port im Standardblockiermodus geöffnet wird, d. h Geben Sie nicht die Option O_NONBLOCK open an. Andernfalls hat O_NONBLOCK Vorrang vor der VMIN- und VTIME-Spezifikation und read () setzt errno an EAGAIN senden und sofort -1 anstelle von 0 zurückgeben, wenn keine Daten verfügbar sind. (Dies ist das Verhalten, das in neueren Linux 3.x-Kerneln beobachtet wird. Ältere 2.6.x-Kernel verhalten sich möglicherweise anders.)

Die Termios-Manpage beschreibt ( c_cc array index) [~ # ~] vmin [~ # ~] als "Mindestanzahl von Zeichen für nicht-kanonisches Lesen" und ( c_cc Array index) [~ # ~] vtime [~ # ~] als "Timeout in Entscheidungssekunden für nicht-kanonisches Lesen" .
[~ # ~] vmin [~ # ~] sollte von Ihrem Programm angepasst werden, um der typischen Nachrichten- oder Datagrammlänge zu entsprechen erwartet und/oder die Mindestgröße für den Abruf und die Verarbeitung von Daten pro read () .
[~ # ~] vZeit [~ # ~] sollte von Ihrem Programm angepasst werden, um die typische Burstiness oder Ankunftsrate von Seriennummern zu berücksichtigen Daten, die erwartet werden und/oder die maximale Wartezeit auf Daten oder ein Datum.

Die [~ # ~] vmin [~ # ~] und [~ # ~] vtime [~ # ~ ] - Werte interagieren, um das Kriterium für die Rückkehr des Lesens zu bestimmen. Ihre genaue Bedeutung hängt davon ab, welche von ihnen ungleich Null sind. Es gibt vier mögliche Fälle.
Diese Webseite erklärt es als:

  • VMIN = 0 und VTIME = 0

    Dies ist ein vollständig nicht blockierender Lesevorgang - der Anruf wird sofort direkt aus der Eingabewarteschlange des Fahrers ausgeführt. Wenn Daten verfügbar sind, werden sie bis zu nByte in den Puffer des Aufrufers übertragen und zurückgegeben. Andernfalls wird sofort eine Null zurückgegeben, um "keine Daten" anzuzeigen. Wir werden bemerken, dass dies ein "Polling" der seriellen Schnittstelle ist und es fast immer eine schlechte Idee ist. Wenn dies wiederholt durchgeführt wird, kann dies enorm viel Prozessorzeit in Anspruch nehmen und ist äußerst ineffizient. Verwenden Sie diesen Modus nur, wenn Sie wirklich genau wissen, was Sie tun.

  • VMIN = 0 und VTIME> 0

    Dies ist eine reine zeitgesteuerte Lesung. Wenn Daten in der Eingabewarteschlange verfügbar sind, werden sie bis zu maximal n Byte in den Puffer des Anrufers übertragen und sofort an den Anrufer zurückgegeben. Andernfalls blockiert der Treiber, bis Daten eingehen oder wenn VTIME-Zehntel nach Beginn des Anrufs ablaufen. Wenn der Timer ohne Daten abläuft, wird Null zurückgegeben. Ein einzelnes Byte reicht aus, um diesen Leseaufruf zu erfüllen. Wenn jedoch mehr in der Eingabewarteschlange verfügbar ist, wird es an den Aufrufer zurückgegeben. Beachten Sie, dass dies ein Gesamttimer ist, kein Zwischenzeichen.

  • VMIN> 0 und VTIME> 0

    Ein read () ist erfüllt, wenn entweder VMIN-Zeichen in den Puffer des Aufrufers übertragen wurden oder wenn VTIME-Zehntel zwischen Zeichen ablaufen. Da dieser Timer erst beim Eintreffen des ersten Zeichens gestartet wird, kann dieser Anruf unbegrenzt blockiert werden, wenn die serielle Leitung frei ist. Dies ist die gebräuchlichste Betriebsart, und wir betrachten VTIME als Zeitüberschreitung zwischen Zeichen und nicht als Gesamtzeitüberschreitung. Dieser Aufruf sollte niemals null gelesene Bytes zurückgeben.

(Nach meiner Erfahrung ist der VMIN>0 and VTIME>0-Modus funktioniert nicht ganz wie angekündigt. Der Timer scheint ein sehr kurzes Intervall zu sein, viel weniger als eine Zehntelsekunde. Ich habe nicht gesehen, dass es auf ARM mit 2.6 und Linux 3.13 auf x86 funktioniert. Bei einer schnellen Baudrate (115200) mit VMIN = 1 und VTIME = 1 wird manchmal read () zurückgegeben 10 oder mehr Bytes. Häufig wird jedoch nur ein Teil von wenigen Bytes gelesen, unabhängig vom VTIME-Wert. Vielleicht ist diese Brokenness bevorzugt/erwünscht. Bei modernen schnellen Baudraten ist eine Nachrichtentrennung von mindestens 0,1 Sekunden einfach zu lang (und nicht praktikabel) .)

  • VMIN> 0 und VTIME = 0

    Dies ist ein gezählter Lesevorgang, der nur dann erfüllt wird, wenn mindestens VMIN-Zeichen in den Puffer des Aufrufers übertragen wurden - es ist keine Timing-Komponente beteiligt. Dieses Lesen kann aus der Eingabewarteschlange des Fahrers (wo der Anruf sofort zurückkehren könnte) oder durch Warten auf das Eintreffen neuer Daten erfolgen: In dieser Hinsicht kann der Anruf auf unbestimmte Zeit blockiert werden. Wir glauben, dass es undefiniertes Verhalten ist, wenn nbytes kleiner als VMIN sind.

Der von Ihnen erwähnte Code konfiguriert den "nicht blockierenden" Modus als VMIN = 0 und VTIME = 5. Dadurch wird read () nicht sofort zurückgegeben, wie dies bei einem nicht blockierenden kanonischen Lesevorgang der Fall wäre. mit diesem Code sollte ein read () immer mindestens eine halbe Sekunde warten, bevor er zurückkehrt. Die herkömmliche Definition eines "Nichtblockierens" ist, dass Ihr aufrufendes Programm während des Systemaufrufs nicht vorbelegt wird und die Kontrolle (fast) sofort zurückerhält. Setzen Sie VMIN = 0 und VTIME = 0, um eine (bedingungslose und) sofortige Rückgabe (für einen nicht kanonischen Lesevorgang) zu erhalten.

57
sawdust