Blockierende Socket-Sys-Calls unterbrechen



  • Hi!

    Was ist die gänge Praxis um portabel blockierende Socket-Sys-Calls wie accept oder recv aus einem anderen Thread zu unterbrechen und den Programmablauf nach dem Sys-Call fortzusetzen (d. h. nicht das ganze Programm oder den aktuellen Thread terminieren zu lassen)?

    Zwei Möglichkeiten mit select, epoll_wait, ... hab ich schon versucht:
    1. Eine Pipe mit in das Set packen und, wenn der Call unterbrochen werden soll, einfach etwas in die Pipe hereinschreiben. Das (auf select bezogen) wäre allerdings nicht portabel, da man unter Windows (wenn man das zur Portabilität zählen darf 😃 ) keine Pipes in das Set packen kann.
    2. Einen Timeout (z. B. 1 Sekunde) festlegen und bei auftretendem Timeout einfach nochmal aufrufen. Das kommt mir aber etwas unsauber vor, da man hier nicht die Möglichkeit hat den Call "sofort" zu unterbrechen, sondern immer noch eine bestimmte Latenz hat.

    Wie siehts mit Prozess-/Thread-Signalen (SIGINT o. ä.) aus?

    Wenn es dafür keine portable Möglichkeit gibt, würde ich unter Unix die 1. Möglichkeit mit der Pipe benutzen. Aber wie siehts da unter Windows aus, gibts da einen Sys-Call der diese anderen Sys-Calls unterbricht?

    Wäre nett, wenn ihr mir auf die Sprünge helfen könntet. 🙂



  • Unter Unix und Linux wir häufig Deine beschriebene Aufweckpipe verwendet. Signale sollte man in multithreaded-Programmen vermeiden. Wie es unter anderen Betriebssystemen aussieht, solltest Du nicht im "Linux/Unix"-Forum fragen.



  • devkid schrieb:

    2. Einen Timeout (z. B. 1 Sekunde) festlegen und bei auftretendem Timeout einfach nochmal aufrufen. Das kommt mir aber etwas unsauber vor, da man hier nicht die Möglichkeit hat den Call "sofort" zu unterbrechen, sondern immer noch eine bestimmte Latenz hat.

    Das kommt ganz auf den Anwendungsfall an. Bei einer Server-Applikation, die normalerweise rund um die Uhr läuft, ist's meist ziemlich schnuppe wenn sie mal 5 Sekunden zum Beenden braucht.
    Unter Windows solltest du nicht mit select arbeiten, eher mit den I/O-Completion-Ports.



  • tntnet schrieb:

    Wie es unter anderen Betriebssystemen aussieht, solltest Du nicht im "Linux/Unix"-Forum fragen.

    Das war nur eine Nebenfrage, hauptsächliche wollte ich die Vorgehensweise unter Unix wissen. 🙂

    Badestrand schrieb:

    Bei einer Server-Applikation, die normalerweise rund um die Uhr läuft, ist's meist ziemlich schnuppe wenn sie mal 5 Sekunden zum Beenden braucht.

    Beim akzeptierenden Socket sollte das wirklich kein Problem sein, aber eventuell bei den Client-Sockets mit recv .

    Badestrand schrieb:

    Unter Windows solltest du nicht mit select arbeiten, eher mit den I/O-Completion-Ports.

    Das war wohl das Stichwort. 😃

    An der Sache mit der Aufweck-Pipe stört mich aber noch etwas:
    Damit müsste ich ja für jeden Client eine Pipe erstellen, womit das 2 zusätzliche File Deskriptoren pro Client wären. Ich glaube irgendwann stoß ich da noch an die Grenze der maximalen File Deskriptoren pro Prozess. 😞



  • tntnet schrieb:

    Signale sollte man in multithreaded-Programmen vermeiden.

    wieso?



  • devkid schrieb:

    An der Sache mit der Aufweck-Pipe stört mich aber noch etwas:
    Damit müsste ich ja für jeden Client eine Pipe erstellen, womit das 2 zusätzliche File Deskriptoren pro Client wären. Ich glaube irgendwann stoß ich da noch an die Grenze der maximalen File Deskriptoren pro Prozess. 😞

    Du solltes ein select oder poll (oder epoll) für den gesamten Server haben, welcher alle Dateidescriptoren (oder Dateideskriptoren 😕 ) überwacht. Dann brauchst Du für diesen genau eine Aufweckpipe, um ihm zu signalisieren, dass er was tun soll.

    Um die eigentliche Arbeit dennoch parallel von mehreren Threads zu verarbeiten, machst Du eine Queue, in die Du alle Dateidescriptoren steckst, auf denene es was zu tun gibt. Ein Poll von Threads wartet auf diese Queue und legt los, sobald es etwas zu tun gibt. Diese Queue wird über ein Event geweckt.

    Alternativ kann der Thread, der das poll ausgeführt hat auch den nächsten Thread das poll übergeben und selbst die Arbeit erledigen. Das kann schneller sein, da der Descriptor, auf dem es was zu tun gibt, nicht erst an einen anderen Thread übergeben werden muss.

    Klingt alles recht kompliziert und das ist es auch. Einen guten multithreaded Server zu schreiben ist durchaus anspruchsvoll.


Anmelden zum Antworten