Select statt read, geht das?



  • Hallo,

    ich möchte einen Treiber Rückgabewert einlesen.
    Mein Vorgehen bisher:

    bytes_read = read( deviceFD, (char *)&buffer32byte, 32);
    

    Nun gibt mir das Gerät aber nicht unbedingt etwas zurück und ich will den read abbrechen. Hierzu würde ich gerne select hernehmen. Allerdings verstehe ich nicht:

    a.) wie setze ich hier die fds auf?
    b.) select kann eigentlich nur Timeouts, ich will über eine Variable oder Mutex oder Ähnlichem den select abbrechen.

    Kann mir hier jemand ein gutes Beispiel nennen, ich finde leider nur Anleitungen für Timeouts 😞

    Grüße
    TheNoName



  • thenoname schrieb:

    a.) wie setze ich hier die fds auf?

    Die Frage verstehe ich nicht. Du setzt mit FD_SET welche File-Descriptoren du überwachen möchtest. Dann rufst du select auf. Sobald select zurück kehrt kannst du mit FD_ISSET überprüfen, ob Daten auf dem entsprechenden Descriptor vorliegen. Falls ja, dann rufst du read auf.

    thenoname schrieb:

    b.) select kann eigentlich nur Timeouts, ich will über eine Variable oder Mutex oder Ähnlichem den select abbrechen.

    Dann hast du zwei Möglichkeiten. Entweder du machst die Timeouts so kurz, dass du immer mal wieder zwischendurch überprüfst, ob die Abbruchvariable gesetzt wurde. Falls ja, dann brichst du ab, falls nein, rufst du erneut select auf.

    Eine andere Möglichkeit wäre, das select noch eine Pipe überwachen lassen, in der du etwas reinschreibst, wenn select abbrechen soll.



  • read() ist doch non-blocking? wie kann man den abbrechen können?

    Wenn Daten anliegen, dann liest er sie aus, andernfalls ein EOF oder einen FD Zugriffsfehler.

    recv() wäre da evt. schon angebrachte, den kannst du aber glaube ich nicht weiter abbrechen. Der ist normalerweise blocking und wartet, bis Daten vorliegen.

    select() überwacht eine Sammlung deiner FDs:
    - Lege eine Variable reading_collection vom Typ "fd_set" an
    - Lege ein struct timeval timeout; an und setze die Variablen dieser auf gewünschten Timeout-Wert
    - Füge mit FD_SET(fd, &reading_collection); deine fd der abzuhörenden Sammlung von FDs bei
    - Rufe select(2, reading_collection, NULL, NULL, timeout) mit deinen Parametern auf
    - Prüfe nach select() auf zu lesende Daten mit FD_ISSET(FD, &reading_collection)
    WENN True, dann lese mit read(FD...) die Daten von der FD
    WENN False, dann führe select() erneut aus



  • Hi, danke für die Antwort!

    Leider fehlt es da an den Grundlagen:
    a.) Wie mache ich einen Filedescriptor, der den obigen read ersetzt?
    b.) Was versteht man unter einer Pipe, wie bastelt man diese in einen FD?

    Bis jetzt fand ich nur Anleitungen z.B. zu select die FD Sets verwenden, aber keine Infos wie man diese für sowas aufsetzen muss.

    Grüße
    TheNoName



  • read () wartet bei mir bis der Datenbuffer (hier 32 byte) gefüllt ist, also blocking. (Treiber abhängig??)
    Ich möchte keine TImeouts verwenden.
    Aber vielleicht kann man mit select auch nur den Trigger darstellen und nicht den read kapseln?



  • Eine FD ist ein FileDeskriptor(hoffe das trifft in Windows auch alles so zu). FileDeskriptoren findest du eigentlich bei ziemlich jeder Verbindung. Eine Pipe hat als Eingang und Ausgang auch nur FD.

    int FD[2];
    FD=pipe();
    

    ProgChilds Idee(Danke da auch von meiner Seite, daran hab ich noch nicht gedacht) war, dass du die lesende FD der Pipe mit in die reading_collection aufnimmst ebenfalls mit
    FD_SET(FD[0], &reading_collection);

    Wie gewünscht mach ich das mal mit Pipes, ich hoffe das ist dann so richtig.

    Wie du(oder jemand von dem du copy&paste) schon in read() ein FD verwendest hast du auch schon einen Filedeskriptor
    ->du nimmst den FD aus read() und steckst ihn mittels FD_SET in die reading_collection Variable.

    fd_set reading_collection;
    int Pipe[2];
    int FD=/*hier fehlt noch deine FD aus read*/
    char output[512];
    int size=0;
    
    Pipe=pipe();
    FD_SET(FD, &reading_collection);
    FD_SET(Pipe[0], &reading_collection);
    
    select((FD>Pipe[0]?FD:Pipe[0])+1, &reading_collection, NULL, NULL, NULL) // FD > Pipe[0] da unter Linux der erste Parameter der größte FD +1 sein soll 
    
    // Select blockiert, bis an einer Leitung Daten vorliegen.
    
    if(FD_ISSET(FD, &reading_collection)&&(size=read(FD, output, 511))>0) { // zweiter Operand wird erst geprüft, wenn erster Operand True und somit Daten vom Treiber kamen
        output[size]=0;
        printf("Empfangen: %s", output);
    } else {
        printf("Ein Fehler ist aufgetreten oder das horchen wurde abgebrochen.")
    }
    

    Du kannst diesen Programm nun abbrechen indem du Daten an:

    write(Pipe[1], "Abbruch", 8);
    

    sendest. ABER ich vemrute das kannst du nur außerhalb deines Programm, da dieses ja blockiert. Um das nun abzubrechen musst du entweder mit einem Kindprozess oder Thread arbeiten. Schau mal was dir da besser gefällt 😉

    Oder du arbeitest mit named Pipes, dann können glaube ich auch schon laufende Prozesse darauf zugreifen.



  • Hi, das sieht schon sehr nach einer Lösung aus.
    Allerdings fürchte ich, das ich hier einen Trigger schaffe "wenn" Daten anliegen. Ich möchte aber nicht nur auf das erste neue Byte warten sondern auf die ersten 32 neuen Bytes.

    Ich arbeite mit mehreren Threads. Allerdings möchte ich es mit einer solchen Methode lösen und nicht den read thread z.B. killen wenn ich read nicht mehr brauche 😉



  • DaRe schrieb:

    Eine FD ist ein FileDeskriptor(hoffe das trifft in Windows auch alles so zu). FileDeskriptoren findest du eigentlich bei ziemlich jeder Verbindung. Eine Pipe hat als Eingang und Ausgang auch nur FD.

    Bzgl. Windows sei angemerkt, dass Windows select nur mit sockets kann.

    thenoname schrieb:

    Hi, das sieht schon sehr nach einer Lösung aus.
    Allerdings fürchte ich, das ich hier einen Trigger schaffe "wenn" Daten anliegen. Ich möchte aber nicht nur auf das erste neue Byte warten sondern auf die ersten 32 neuen Bytes.

    Dann wirst du wohl select so lange aufrufen müssen, bis du 32-Byte gelesen hast.

    Mir fällt gerade noch ein. Du wartest auf ein Device. Soweit ich weiß, kann Linux kein Non-Blocking-IO für Disk-IO. Siehe [http://fixunix.com/linux/556237-asynchronous-disk-io-linux.html]. Ich weis jetzt nicht, wie das mit Devices aussieht, da müsstest du dich mal schlau machen.

    Für Grundlagen mit select , kannst du dir man: select_tut oder das Zotteljedi Tutorial ansehen [http://www.zotteljedi.de/socket-tipps/index.html].



  • Hi,

    also ich arbeite in Linux, nicht Windows! Bei Sockets verwende ich selects nur mit Timeouts.
    Das mit dem Select macht Probleme, da das Select asynchron zum Kerneltreiber arbeitet. d.h. mein Linux kann alle 20ms einen Thread supporten und wenn ich jetzt den select abwarte und einen read anhänge verliere ich Daten weil das Device mit 10MHz reinschaufelt und zwischen IRQ dem select UND read Zeit liegt. Klug wäre es nun, wenn der Treiber DMA nicht meinen read buffer als DMA buffer mappen würde und einen eigenen Buffer hätte 😞
    Hat er aber nicht.
    Vom Prinzip werd ich wohl dem Treiber einen Buffer reinbauen müssen.

    Grüße
    TheNoName



  • Dann schau mal bei http://www.zotteljedi.de/socket-tipps/ nach.

    Und das & bei (char *)&buffer32byte sieht etwas merkwürdig aus.


Log in to reply