web-dev-qa-db-de.com

Geben Sie den UDP-Port frei, der vom Dead-Prozess unter OS X verwendet wird

Ich arbeite unter OS X 10.11.6 und versuche, ein Programm auszuführen, das beim Start normalerweise den UDP-Port 8008 abhört.

Dieses Programm erzeugt normalerweise auch einige untergeordnete Hilfsprozesse während seiner Ausführung, der Port ist jedoch an den übergeordneten Prozess gebunden.

Leider bleibt der Port beim Beenden des Programms manchmal offen, obwohl das Programm (Eltern + Kinder) nicht mehr existiert.

In diesem Fall schlägt der Versuch, das Programm erneut auszuführen, natürlich mit dem Fehler EADDRINUSE fehl. In diesen Fällen war die einzige Lösung, die ich gefunden habe, ein Neustart des Computers.

Es fällt mir schwer zu glauben, dass ich den Port ohne einen Neustart nicht freigeben kann.

Hier sind einige Diagnosen, die ich bisher ausgeführt habe (ich habe alle diese mit und ohne Sudo ausgeführt):

Suchen Sie den Prozess mithilfe von Port 8008 mit lsof:

$ lsof -i -n -P | grep UDP | grep 8008

Aber überraschenderweise gibt es keine Ergebnisse.

Ich hatte jedoch mehr Glück mit netstat:

$ netstat -tulnvp udp | grep 8008
udp4  0  0  *.8008    *.*    196724   9216  47205   0

Der Port ist also in der Tat gebunden, und der Schuldige ist pid 47205.

$ ps aux | grep 47205

Gibt nichts zurück. Dasselbe gilt für die PIDs 47206 und 47207 (mit Sicherheit die den Kindern zugewiesenen PIDs). Ich habe auch andere Variationen der grep ausprobiert (Programmname, Pfad usw.).

Ich habe auch nach einem Prozess gesucht, der 47205 als übergeordnetes Element meldet:

$ ps -axo pid,ppid,command | grep 47205

So sind die Kinderprozesse auch eindeutig tot.

Da ich nicht in der Lage war, kill irgendetwas zu ändern, habe ich versucht, launchd zu SIGHUPEN, in der Hoffnung, dass dadurch alle untergeordneten Zombie-Prozesse entfernt werden:

$ Sudo kill HUP 1
$ Sudo kill -s HUP 1

Leider zeigt netstat immer noch den gebundenen Port.

Zuletzt habe ich versucht, die Loopback-Schnittstelle neu zu starten:

$ Sudo ifconfig lo down
$ Sudo ifconfig lo up

Aber auch hier ohne Wirkung.

Ich habe mehrere Stunden gewartet, seit das Programm das letzte Mal lief, daher bin ich mir ziemlich sicher, dass es inzwischen zu einem Timeout gekommen wäre, aber der Port wird einfach nicht freigegeben.

Irgendwelche Ideen, wie Sie die Freigabe des Ports ohne Neustart erzwingen können?

Bearbeiten:

  • Das fragliche Programm ist das elektronenumhüllte Patchwork .
  • Diese Frage stammt aus dieser Github Ausgabe .
  • Obwohl es ideal wäre, eine Lösung/einen Bugfix zu finden, der das Auftreten des Problems verhindert, bin ich auch an Möglichkeiten interessiert, diesen Port manuell vom Terminal aus zu schließen
26
ktorn

Rufen Sie in Ihrem Code nach dem Erstellen des Sockets, aber vor dem Aufruf von bind Folgendes auf:

int val = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

Rufen Sie dann bind auf. Mit den oben genannten Schritten kann die Socketbindung auch dann erfolgreich durchgeführt werden, wenn der Port verwendet wird.

Zwei Prozesse, die versuchen, eine recvfrom auf demselben Port zu speichern, führen dazu, dass einer der Prozesse das Paket empfängt, der andere jedoch nicht. Und es ist nicht deterministisch, welche man will. Stellen Sie also sicher, dass nicht zwei Prozesse ordnungsgemäß ausgeführt werden und den Port gemeinsam nutzen.

3
selbie

Es ist in der Tat möglich, den Port manuell zu schließen, ohne den Computer neu zu starten. Unter verschiedenen Linux-Versionen geschieht dies normalerweise mit GDB, indem Syscalls ausgegeben werden, die sich als der Prozess tarnen (z. B. close(fd)syscall im Sockets-Dateideskriptor).

Der Prozess dafür:

  • Öffnen Sie einen UDP-Port: netcat -u 127.0.0.1 33333.
  • Überprüfen Sie den UDP-Port: netstat -npu (u for UDP). Dadurch erhalten Sie die PID, die diesen Port belegt.
  • Führen Sie Folgendes aus: lsof -np $pid für diese PID, um den Filedescriptor für den Socket abzurufen.
  • Führen Sie dann GDB für diese PID aus: Sudo gdb -p 73599
  • Wenn in GDB ausgeführt wird, call close(file_descriptor)

Beispiel:

COMMAND   PID  USER   FD   TYPE   DEVICE SIZE/OFF     NODE NAME
netcat  73599 ubunt  cwd    DIR  259,2     4096 13895497 /home/ubunt/Downloads
netcat  73599 ubunt  rtd    DIR  259,2     4096        2 /
netcat  73599 ubunt  txt    REG  259,2    31248 28835938 /bin/nc.openbsd
netcat  73599 ubunt  mem    REG  259,2    47600 23990813 /lib/x86_64-linux-gnu/libnss_files-2.23.so
netcat  73599 ubunt  mem    REG  259,2  1868984 23990714 /lib/x86_64-linux-gnu/libc-2.23.so
netcat  73599 ubunt  mem    REG  259,2   101200 23990866 /lib/x86_64-linux-gnu/libresolv-2.23.so
netcat  73599 ubunt  mem    REG  259,2    81040 23990710 /lib/x86_64-linux-gnu/libbsd.so.0.8.2
netcat  73599 ubunt  mem    REG  259,2   162632 23990686 /lib/x86_64-linux-gnu/ld-2.23.so
netcat  73599 ubunt    0u   CHR 136,19      0t0       22 /dev/pts/19
netcat  73599 ubunt    1u   CHR 136,19      0t0       22 /dev/pts/19
netcat  73599 ubunt    2u   CHR 136,19      0t0       22 /dev/pts/19
netcat  73599 ubunt    3u  IPv4 22142418    0t0      UDP 127.0.0.1:45255->127.0.0.1:33333

Dann GDB:

$Sudo gdb -p 73599
...
(gdb) call close(3u)
$1 = 0

Sie werden feststellen, dass der Hafen nicht mehr vorhanden ist:

[email protected]:~$ lsof -np 73599
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
netcat  73599 ubunt  cwd    DIR  259,2     4096 13895497 /home/ubunt/Downloads
netcat  73599 ubunt  rtd    DIR  259,2     4096        2 /
netcat  73599 ubunt  txt    REG  259,2    31248 28835938 /bin/nc.openbsd
netcat  73599 ubunt  mem    REG  259,2    47600 23990813 /lib/x86_64-linux-gnu/libnss_files-2.23.so
netcat  73599 ubunt  mem    REG  259,2  1868984 23990714 /lib/x86_64-linux-gnu/libc-2.23.so
netcat  73599 ubunt  mem    REG  259,2   101200 23990866 /lib/x86_64-linux-gnu/libresolv-2.23.so
netcat  73599 ubunt  mem    REG  259,2    81040 23990710 /lib/x86_64-linux-gnu/libbsd.so.0.8.2
netcat  73599 ubunt  mem    REG  259,2   162632 23990686 /lib/x86_64-linux-gnu/ld-2.23.so
netcat  73599 ubunt    0u   CHR 136,19      0t0       22 /dev/pts/19
netcat  73599 ubunt    1u   CHR 136,19      0t0       22 /dev/pts/19
netcat  73599 ubunt    2u   CHR 136,19      0t0       22 /dev/pts/19

GDB ist für MacOS verfügbar, daher sollte es auch für Ihren Fall funktionieren.

Das System kann den Socket geöffnet lassen, bis der E/A-Prozess noch läuft. Auch wenn der Prozess gestorben ist, aber nicht explizit den Socket geschlossen hat. Wenn Ihre Steckdose zu bestimmten Zeiten nicht geschlossen ist, fehlt Ihnen höchstwahrscheinlich etwas. Versuchen Sie, eine Kerneluntersuchung auf niedriger Ebene anstelle von Anwendungen auf höchster Ebene wie netstat oder lsof zu verwenden.

Haftungsausschluss

Ich bin kein OS X-Experte und die meisten Befehle für Linux. Ich lasse es immer noch dort, wenn jemand anderes das gleiche Problem hat.

1. Versuchen Sie festzustellen, ob der Socket noch aktiv ist (optional)

Ich kann vorschlagen, die Socket-Kommunikation zu überprüfen.

 tcpdump -A -s0 port 8080  and tcpdump -A -s0 -ilo port 8080

Wenn Sie Daten sehen, die über Socket übertragen wurden, können Sie sicher sein, dass der Prozess aktiv ist. Oder kann eines seiner Kinder sein. Später können Sie die PID mit strace fangen

2. Überprüfen Sie den Prozess und seinen Status

Linux haben wunderbare procfs. Sie können so viele Dinge von dort bekommen. Und sicher können Sie alle geöffneten Dateideskriptoren sehen

ls -al  /proc/47205/fd

Wenn Sie die Ausgabe sehen und / proc/47205 existiert, wird die nicht freigegebene PID trotzdem angezeigt ps. Sie sehen alle geöffneten Dateien und ihre fds. Es sieht aus wie

133 -> Socket: [32242509]

Wobei 133 eine fd-Zahl ist

Leider hat OS X kein/proc-Dateisystem. Den alternativen Befehl habe ich gefunden.

procexp 47205 fds

Aber ich bin nicht sicher, ob es 100% funktioniert.

3. Schließen des Dateideskriptors (Sockets) in einem anderen Prozess

Unter Linux gibt es Nice-Befehle

fuser -k -n udp 8080

Dadurch werden alle Prozesse, die den Port blockieren, explizit geschlossen. Es scheint, OS X kann Fixiereinheit auch haben

Eine weitere Möglichkeit für echte Hacker besteht darin, eine Verbindung zum Prozess mit gdb herzustellen und Befehle innerhalb des Prozesses auszuführen, da die Dateideskriptornummern nur in der Prozessumgebung gültig sind, genau wie @Mindaugas Bernatavičius schrieb:

gdb -p 47205
>call shutdown([fd_number],2)
>call close([fd_number])

Es gibt einen dritten Weg , wenn möglich, können Sie einfach das gesamte Netzwerk neu starten. Bitte beachten Sie, nur Loopback-Schnittstelle nach unten und oben ist nicht genug. Unter Linux laufen

systemctl restart network  

4.Was tun, um zu verhindern, dass der Socket im System hängen bleibt

Sie sollten immer sicherstellen, dass die Socke geschlossen ist, bevor Sie Ihr Programm beenden. Ich habe viele Probleme mit nodejs gesehen dass Sockets geöffnet bleiben. Durch Aufrufen von Socket.destroy () wird das Problem behoben

Möglicherweise geben Sie hier Ihren Socket-Destroy-Code ein, bevor Sie die App beenden:

app.on ('close', Funktion (Code) {

// Benutzer hat die App geschlossen. Beenden Sie den Host-Prozess.

process.exit ();

});

1
Daniel

eine verwandte Frage: mac hat das Verhalten von SO_REUSEADDR und SO_REUSEPORT geändert:

Verhalten von SO_REUSEADDR und SO_REUSEPORT geändert?

und ich bin der Betreuer von iptux [1]. Wenn ich SO_REUSEPORT verwende, kann das Programm gestartet werden, aber ich kann keine Nachrichten von diesem Port empfangen. Alle Nachrichten werden als schwarzes Loch an den nicht geschlossenen Port gesendet.

[1] https://github.com/iptux-src/iptux

0
lidaobing

Ihre Frage sieht ungefähr so ​​aus:


Wie du gesagt hast:

Zuletzt habe ich versucht, die Loopback-Schnittstelle neu zu starten:

$ Sudo ifconfig lo down

$ Sudo ifconfig lo up

Haben Sie versucht, alle verfügbaren Netzwerkschnittstellen (LAN oder WLAN) und nicht nur den Loopback neu zu starten?

Anstelle von ifconfig können Sie auch das native MacOS-Befehlsprogramm (von hier ) verwenden, um das Gerät selbst auszuschalten und dann einzuschalten (en0 an your device name anpassen):

networksetup -setairportpower en0 off
networksetup -setairportpower en0 on

Sie können auch endlich versuchen, DHCP freizugeben und zu erneuern mit:

Sudo dhclient -v -r

Grüße

0
A STEFANI