Wie implementiert man ein Timeout im read() Funktionsaufruf?

Lesezeit: 5 Minuten

Benutzeravatar von domlao
domlao

Ich möchte den seriellen COM-Port für die Kommunikation verwenden und jedes Mal, wenn ich den anrufe, ein Timeout implementieren lesen() Funktionsaufruf.

int filedesc = open( "dev/ttyS0", O_RDWR );

read( filedesc, buff, len );

BEARBEITEN:

Ich verwende Linux OS. Wie implementiert man den Select-Funktionsaufruf?

Benutzeravatar von Puppe
Puppe

select() benötigt 5 Parameter, zuerst den höchsten Dateideskriptor + 1, dann ein fd_set zum Lesen, eines zum Schreiben und eines für Ausnahmen. Der letzte Parameter ist ein Struktur-Zeitwert, der für die Zeitüberschreitung verwendet wird. Es gibt -1 bei einem Fehler, 0 bei Zeitüberschreitung oder die Anzahl der Dateideskriptoren in den festgelegten Sätzen zurück.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>

int main(void)
{
  fd_set set;
  struct timeval timeout;
  int rv;
  char buff[100];
  int len = 100;
  int filedesc = open( "dev/ttyS0", O_RDWR );

  FD_ZERO(&set); /* clear the set */
  FD_SET(filedesc, &set); /* add our file descriptor to the set */

  timeout.tv_sec = 0;
  timeout.tv_usec = 10000;

  rv = select(filedesc + 1, &set, NULL, NULL, &timeout);
  if(rv == -1)
    perror("select"); /* an error accured */
  else if(rv == 0)
    printf("timeout"); /* a timeout occured */
  else
    read( filedesc, buff, len ); /* there was data to read */
  close(filedesc);
}

  • Diese Lösung ist nicht gut genug, denn wenn wir auf 5 Bytes warten, aber rechtzeitig nur 1 erhalten, ist die Auswahl damit in Ordnung und wir werden beim Lesen für immer blockieren.

    – Roskoto

    28. September 2012 um 13:05 Uhr


  • Nein würde es nicht. Wenn nur ein Byte verfügbar wäre und Sie versuchen, 5 zu lesen, würde read() nicht blockieren, sondern 1 zurückgeben (Anzahl der gelesenen Bytes). Aus der Read-Manpage: „Es ist kein Fehler, wenn diese Zahl kleiner als die Anzahl der angeforderten Bytes ist; dies kann beispielsweise passieren, weil derzeit tatsächlich weniger Bytes verfügbar sind (vielleicht weil wir uns dem Ende der Datei näherten , oder weil wir von einer Pipe oder von einem Terminal lesen) oder weil read() durch ein Signal unterbrochen wurde.”

    – Puppe

    10. Oktober 2012 um 7:09 Uhr


  • und wie man immer wieder “auswählt”, das zweite Mal wird sofort zurückkehren. Ich will wissen warum?

    – Känguru

    1. Februar 2015 um 4:02 Uhr

  • @kangear siehe man select und es wird erklärt timeval Timeout-Funktion. Kurz gesagt, heißt es “Betrachten Sie das Timeout als undefiniert, nachdem select() zurückkehrt.” und das bedeutet, dass Sie es nicht wiederverwenden sollten, wenn Sie nicht wirklich sicher sind, was (und wo) Sie tun.

    – Sampo Sarrala – codidact.org

    18. Oktober 2015 um 16:37 Uhr


  • Das ursprüngliche Poster gab an, dass er Linux ausführte, und die Frage ist mit Linux markiert, also habe ich eine Antwort gegeben, die unter Linux funktioniert.

    – Puppe

    31. Januar 2017 um 5:54 Uhr

Benutzeravatar von caf
Café

Als Alternative zu select()für den speziellen Fall einer seriellen Schnittstelle (Terminal), die Sie verwenden können tcsetattr() um den Dateideskriptor mit einem Lese-Timeout in den nicht-kanonischen Modus zu versetzen.

Deaktivieren Sie dazu die ICANON Flag, und setzen Sie die VTIME Steuerzeichen:

struct termios termios;

tcgetattr(filedesc, &termios);
termios.c_lflag &= ~ICANON; /* Set non-canonical mode */
termios.c_cc[VTIME] = 100; /* Set timeout of 10.0 seconds */
tcsetattr(filedesc, TCSANOW, &termios);

Notiz VTIME in Zehntelsekunden gemessen wird und dass der dafür verwendete Typ typischerweise ein ist unsigned charwas bedeutet, dass das maximale Timeout 25,5 Sekunden beträgt.

  • In meinem Fall musste ich auch VMIN auf 0 setzen: termios.c_cc[VMIN] = 0.

    – João MS Silva

    4. Dezember 2014 um 22:28 Uhr


  • Angenommen, wir haben zwei Geräte TxDev und RxDev, die bei t = T1 über UART kommunizieren, TxDev beginnt, Daten zu senden, zu diesem Zeitpunkt erreicht RxDev die Leseanweisung noch nicht, gehen die ersten von TxDev gesendeten Bytes verloren?, oder RxDev hat Ein Puffer, in dem empfangene Daten gespeichert werden, wird nicht aufgerufen ?

    – Fed

    1. Juli 2016 um 8:03 Uhr

  • @fedi: Unter Linux werden sie vom Kernel gepuffert.

    – Café

    1. Juli 2016 um 12:37 Uhr

Wenn Sie den Socket so einstellen, dass er im nicht blockierenden Modus betrieben wird, liest jeder Aufruf von read nur die aktuell verfügbaren Daten (falls vorhanden). Dies ist also effektiv gleichbedeutend mit einem sofortigen Timeout.

Sie können den nicht blockierenden Modus für einen Socket mit einer Funktion wie der folgenden festlegen:

int setnonblock(int sock) {
   int flags;
   flags = fcntl(sock, F_GETFL, 0);
   if (-1 == flags)
      return -1;
   return fcntl(sock, F_SETFL, flags | O_NONBLOCK);
}

(Weitere Informationen zum Lesen von nicht blockierenden Sockets finden Sie in der read Manpage)

Sie sagen nicht, was das Betriebssystem ist, aber wenn Sie unter Linux laufen, könnten Sie den select-Aufruf verwenden. Es kehrt zurück, wenn im Dateideskriptor etwas zu lesen ist, oder Sie können es so einrichten, dass es abläuft, wenn nichts zu lesen ist. Der Rückgabecode gibt an, welche.

Benutzeravatar von rick-rick-rick
Rick-Rick-Rick

Der folgende Code verwendet Millisekunden-Timeouts pro Zeichen. Ich verwende es in einem meiner Projekte, um vom COM-Port zu lesen.

size_t TimeoutRead (int port, void*buf, size_t size, int mlsec_timeout)
{
    struct pollfd fd = { .fd = port, .events = POLLIN };

    size_t      bytesread = 0;

    while (poll (&fd, 1, mlsec_timeout) == 1)
    {
        int chunksize = read (port, buf + bytesread, size);
        if (chunksize == -1)
            return -1;

        bytesread += chunksize;
        size -= chunksize;

        if (size == 0)
            return bytesread;
    }

    // TODO: IsTimeout = true;
    return bytesread;
}

  • Sie müssen revents auf 0 zurücksetzen, bevor Sie die Abfrage erneut aufrufen

    – dr_begemot

    5. Dezember 2019 um 8:01 Uhr

  • @dr_begemot Würdest du das sagen poll() Beispiel hier ist falsch wie .revent vorher nicht genullt poll() Anruf?

    – chux – Wiedereinsetzung von Monica

    10. Juni 2020 um 22:07 Uhr

  • Sie müssen revents auf 0 zurücksetzen, bevor Sie die Abfrage erneut aufrufen

    – dr_begemot

    5. Dezember 2019 um 8:01 Uhr

  • @dr_begemot Würdest du das sagen poll() Beispiel hier ist falsch wie .revent vorher nicht genullt poll() Anruf?

    – chux – Wiedereinsetzung von Monica

    10. Juni 2020 um 22:07 Uhr

1413730cookie-checkWie implementiert man ein Timeout im read() Funktionsaufruf?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy