C-Socket-Blockierungsaufruf

8

Ich wundere mich über das Verhalten von Socket bei blockierenden und nicht blockierenden Aktionen. Was passiert mit Threads, die auf Sockets blockieren, wenn sich der Socket-Blocking-Modus ändert? Hier ist das Szenario; thread1 (T1) erstellt einen UDP-Socket und

%Vor%

T1 wartet (schläft) für Empfang

%Vor%

und thread2 (T2) ändern den Socket-Modus auf nicht blockierend, bevor Socket irgendwelche Daten empfängt

%Vor%

Was passiert mit T1? Wird es signalisiert / geweckt, weil der Socket nicht mehr blockiert?

    
Qxtrml 11.08.2017, 13:00
quelle

4 Antworten

8

Das Verhalten ist buchstäblich nicht spezifiziert: fcntl ist nicht erforderlich, um Threads freizugeben.

Linux setzt nur die Flagge in der Dateibeschreibung struct file und gibt zurück, ohne blockierte Threads zu entsperren.

Ein Thread bereits blockiert in recv kann so geplant werden, dass er nur ausgeführt wird, wenn:

  • zu lesende Daten werden verfügbar;
  • oder eine Fehlerbedingung für den Dateideskriptor erkannt wird ( FIN / RST , Socket-Lesezeitlimit, TCP-Keepalive-Fehler, der Dateideskriptor ist close d von einem anderen Thread);
  • oder ein Signal wird empfangen und die Signaldisposition enthält nicht SA_RESTART ;
  • oder es ist pthread_cancel led.

Die Tatsache, dass Sie versuchen, die Flags eines Dateideskriptors eines anderen Threads zu ändern, deutet darauf hin, dass Ihr Design einer Überprüfung bedarf. Idealerweise dürfen Threads keine Daten gemeinsam nutzen und nicht den Zustand des jeweils anderen anstoßen. Stattdessen sollten sie die Nachrichtenübergabe verwenden, um miteinander zu kommunizieren.

    
Maxim Egorushkin 11.08.2017, 13:30
quelle
3
Du hast mich neugierig gemacht. Erstens ist es ziemlich offensichtlich, dass, da es keinen Standard gibt, der angibt, dass der Socket aufwachen sollte, er nicht geweckt wird, weil es ziemlich mühsam wäre, dies zu implementieren (da das nicht blockierende Flag in einer anderen Schicht ist als das Socket blockiert). Wir können daher ziemlich sicher sagen, dass die Steckdose nicht aufwachen wird, bis wir ein Paket erhalten. Oder wird es?

%Vor%

Der Code oben (Sorry, konnte es nicht kürzer machen). Blockiert den Thread in recv, wartet ein wenig und setzt anschließend den Dateideskriptor nicht blockierend. Wie erwartet passiert nichts. Aber dann habe ich eine Wendung hinzugefügt. Der Empfänger-Thread hat einen Signal-Handler, der jede Sekunde mit SA_RESTART aufwacht. Natürlich, da wir SA_RESTART recv nicht aufwachen haben.

Unter OSX wird sich dies "schlecht benehmen", wenn wir irgendein Verhalten für richtig halten. Ich bin mir ziemlich sicher, dass sich das auf allen BSDs genauso verhält. Gleiches gilt für Linux. In der Tat, die Art und Weise, wie SA_RESTART normalerweise implementiert wird, bin ich mir ziemlich sicher, dass dies sich ziemlich überall "schlecht benehmen" wird.

Lustig. Dies bedeutet natürlich nicht, dass der obige Code für irgendetwas nützlich ist, aber es ist eine interessante Kuriosität. Um Ihre Frage zu beantworten, ist dies nicht spezifiziert, aber in den meisten Fällen wird es nicht aufwachen, es sei denn, es wird. Bitte mach keine komischen Dinge wie diese.

    
Art 11.08.2017 14:38
quelle
1

Ich denke POSIX-Spezifikation ist ziemlich klar darüber:

  

Wenn keine Nachrichten am Socket verfügbar sind und O_NONBLOCK nicht auf den Dateideskriptor des Sockets gesetzt ist, blockiert recv() , bis eine Nachricht eintrifft. Wenn keine Nachrichten am Socket verfügbar sind und O_NONBLOCK auf den Dateideskriptor des Sockets gesetzt ist, wird recv() fehlschlagen und errno auf [EAGAIN] oder [EWOULDBLOCK] setzen.

Sie rufen recv auf, wenn O_NONBLOCK noch nicht gesetzt ist, bis die Nachricht eintrifft (erst wenn sich der Modus ändert).

    
myaut 11.08.2017 13:46
quelle
0
Der

Nicht blockierte Thread (blockierte noch nicht, weil er gelesen wurde, als er sich im Blocking-Modus befand) verhält sich so, als wäre er immer ein nicht blockierender Modus.

Lesen recv (2) Handbuchseite

  

Wenn keine Nachrichten am Socket verfügbar sind und der Socket nicht blockiert ist, wird der Wert -1 zurückgegeben und der externe Wert          Variable errno wird auf EAGAIN oder EWOULDBLOCK gesetzt.

Blockierte Threads (blockiert während des Lesens im Blocking-Modus), bevor Sie in nicht blockierende Threads wechseln. Wie von @Maxim gezeigt, der den Code der Funktion teilt, die keine Threads aufweckt, werden die blockierten Threads erst nach einem Schreibvorgang (Daten sind verfügbar) aktiviert.

    
Tony Tannous 11.08.2017 13:04
quelle