web-dev-qa-db-ja.com

OSXのデッドプロセスで使用されているUDPポートを解放します

私はOSX 10.11.6を使用しており、起動時に通常はUDPポート8008でリッスンするプログラムを実行しようとしています。

このプログラムは通常、操作中にいくつかのヘルパー子プロセスも生成しますが、ポートは親プロセスによってバインドされます。

残念ながら、プログラムを終了すると、プログラム(親+子)が存在しなくなっても、ポートが開いたままになることがあります。

これが発生した場合、プログラムを再度実行しようとすると、当然EADDRINUSEエラーで失敗します。これらの場合、何を試しても、私が見つけた唯一の解決策はマシンを再起動することでした。

再起動せずにポートを解放できないと信じるのに苦労しています。

これまでに実行したいくつかの診断を次に示します(これらすべてをSudoの有無にかかわらず実行しました)。

ポート8008lsofを使用してプロセスを見つけます。

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

しかし、驚くべきことに、結果は返されません。

しかし、私はnetstatでもっと運が良かった:

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

したがって、ポートは実際にバインドされており、原因はpid 47205ですが、次のようになります。

$ ps aux | grep 47205

何も返しません。 PID 4720647207(最も確実に子に割り当てられたPID)についても同じことが言えます。 grepの他のバリエーション(プログラム名、パスなど)も試しました。

また、親として47205を報告するプロセスも探しました。

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

したがって、子プロセスも明らかに死んでいます。

何もkillできないので、ゾンビの子プロセスを削除する可能性があることを期待して、launchdをSIGHUPしようとしました。

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

しかし残念ながら、netstatはまだポートバウンドを示しています。

最後に、ループバックインターフェイスを再起動しようとしました。

$ Sudo ifconfig lo down
$ Sudo ifconfig lo up

しかし、繰り返しますが、効果はありません。

プログラムが最後に実行されてから数時間待っていたので、タイムアウトが発生したことは間違いありませんが、ポートは解放されません。

再起動せずにポートを強制的に解放する方法についてのアイデアはありますか?

編集:

  • 問題のプログラムは、電子でラップされた パッチワーク です。
  • この質問はこれから発生します github issue
  • そもそも問題の発生を防ぐ解決策/バグ修正を見つけるのが理想的ですが、ターミナルからそのポートを手動で閉じる方法にも興味があります
30
ktorn

マシンを再起動せずに手動でポートを閉じることは確かに可能です。さまざまなLinuxフレーバーでは、これは通常、プロセスを装ったsyscallを発行することによってGDBで実行されます(たとえば、ソケットファイル記述子のclose(fd) syscall)。

そのためのプロセス:

  • UDPポートを開きます:_netcat -u 127.0.0.1 33333_。
  • UDPポートを確認します:netstat -npu (u for UDP)。これにより、そのポートを占有するPIDが得られます。
  • そのPIDに対して:_lsof -np $pid_を実行して、ソケットのファイル記述子を取得します。
  • 次に、そのPIDに対してGDBを実行します:_Sudo gdb -p 73599_
  • GDB内でcall close(file_descriptor)を実行します

例:

_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
_

次にGDB:

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

ポートがもう存在しないことがわかります。

_ubunt@ubunt-MS-7A94:~$ 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はMacOSで利用できるので、あなたのケースでも機能するはずです。

コードで、ソケットを作成した後、bind呼び出しの前に、以下を呼び出します。

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

次に、bindを呼び出します。上記により、ポートが使用中の場合でもソケットバインドが成功します。

同じポートでrecvfromを試行する2つのプロセスでは、一方のプロセスがパケットを受信し、もう一方のプロセスは受信しません。そして、どちらを選択するかは決定論的ではありません。したがって、実際に2つのプロセスが合法的に実行され、ポートを共有していないことを確認してください。

3
selbie

システムは、I/Oプロセスがまだ進行中になるまでソケットを開いたままにする場合があります。プロセスが停止したが、明示的にソケットを閉じていない場合でも。ソケットが何時間も閉じられていない場合は、おそらく何かが足りません。 netstatやlsofなどのトップレベルのユーティリティではなく、低レベルのカーネル調査を使用してみてください。

免責事項

私はOSXの専門家ではなく、Linux用のほとんどのコマンドです。他の誰かが同じ問題を抱えている場合、私はまだそこに置いておきます。

1。ソケットがまだ生きているかどうかを確認してください(オプション)

ソケット通信を確認することをお勧めします。

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

ソケットを介して転送されたデータが表示された場合は、プロセスがアクティブであることを確認できます。またはその子の1つである可能性があります。後で strace でpidをキャッチできます

2。プロセスとそのステータスを確認してください

Linuxには素晴らしいprocfsがあります。そこからたくさんのものを手に入れることができます。そして、開いているすべてのファイル記述子を確認できます

ls -al  /proc/47205/fd

出力が表示され、/ proc/47205が存在する場合でも、解放されていないpidはpsと表示されます。開いているすべてのファイルとそのfdsが表示されます。

133->ソケット:[32242509]

ここで、133はfd番号です。

残念ながら、OSXには/ procファイルシステムがありません。私が見つけた代替コマンド。

procexp 47205 fds

しかし、100%機能するかどうかはわかりません。

3。別のプロセスでファイル記述子(ソケット)を閉じる

LinuxにはNiceコマンドがあります

fuser -k -n udp 8080

これにより、ポートをブロックしているすべてのプロセスが明示的に閉じられます。 OS Xのようです フューザーもあるかもしれません

@MindaugasBernatavičiusが書いたように、ファイル記述子番号はプロセス環境でのみ有効であるため、もう1つの実際のハッカーの方法は、gdbを使用してプロセスに接続し、プロセス内でコマンドを実行することです。

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

3番目の方法があり、可能な場合はネットワーク全体を再起動できます。ループバックインターフェイスだけでは十分ではありません。 Linuxで実行

systemctl restart network  

4。システムでソケットがスタックするのを防ぐために何をすべきか

プログラムが終了する前に、常にsockedが閉じていることを確認する必要があります。 nodejsで多くの問題が発生しました ソケットは開いたままです。 Socket.destroy()を呼び出すと問題が解決します

アプリを終了する前に、ここにソケット破棄コードを配置できます。

app.on( 'close'、function(code){

//ユーザーがアプリを閉じました。ホストプロセスを強制終了します。

process.exit();

});

1
Daniel

1つの関連する質問:macはSO_REUSEADDRとSO_REUSEPORTの動作を変更しました:

SO_REUSEADDRとSO_REUSEPORTの動作が変更されましたか?

私はiptux [1]のメンテナです。SO_REUSEPORTを使用すると、プログラムを起動できますが、このポートからメッセージを受信できず、すべてのメッセージがブラックホールとして閉じられていないポートに送信されます。

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

0
lidaobing

あなたの質問は次のようになります:


あなたが言ったように:

最後に、ループバックインターフェイスを再起動しようとしました。

$ Sudo ifconfig lo down

$ Sudo ifconfig lo up

ループバックだけでなく、使用可能なすべてのネットワークインターフェイス(lanまたはwlan)を再構築しようとしましたか?

ifconfigの代わりに、ネイティブMacOSコマンドユーティリティ( ここ から)を使用して、デバイス自体の電源をオフにしてからオンにすることもできます(adapt en0からyour device name):

networksetup -setairportpower en0 off
networksetup -setairportpower en0 on

最後に、次の方法でDHCPのリリースと更新を試みることもできます。

Sudo dhclient -v -r

よろしく

0
A STEFANI