はじめに (対象読者・この記事でわかること)
本記事は、Linuxサーバを運用・管理しているエンジニア、またはネットワークトラブルシューティングに携わる方を対象としています。
netstat -lunp で同一のUDP待ち受けポートが 異なる PID として複数表示される現象に遭遇したことはありませんか?本記事を読むことで、以下が理解でき、実際に原因を特定し、適切に対処できるようになります。
- 同一ポートが複数 PID に紐付く仕組み(SO_REUSEPORT や systemd の socket activation など)
- 具体的な調査手順とコマンド例
- よくある落とし穴とその回避策
この記事を書いた背景は、社内でトラブル対応中に「同じポートが二つのプロセスにバインドされている」ケースが頻発し、原因がすぐに分からず時間を浪費した経験です。その経験を共有し、皆さんのデバッグ時間を短縮したいと思い執筆しました。
前提知識
この記事をスムーズに読み進めるために、以下の知識があると望ましいです。
- Linux の基本的なコマンド操作(ps、lsof、netstat、ss など)
- UDP と TCP の基本的な違い、ポート番号の概念
- システムサービスの管理(systemd、init.d など)
UDPポートが複数PIDで表示されるメカニズム(概要・背景)
Linux カーネルは 同一ポートに対して複数のソケットがバインドできる 仕組みを提供しています。代表的なのが以下の二つです。
-
SO_REUSEPORT
アプリケーションがsetsockopt(..., SO_REUSEPORT, ...)を使用すると、同一のポート番号で複数のプロセスが同時に受信ソケットを作成できます。カーネルは受信パケットをハッシュやラウンドロビンで各ソケットに振り分けるため、負荷分散やマルチスレッド/マルチプロセスサーバに有用です。 -
systemd の Socket Activation
systemd が提供する「socket activation」では、systemdがあらかじめポートをオープンし、要求が来たときに対象サービスを起動します。この場合、systemd 自身がポートを保持 し、実際のサービスプロセスは別の PID でバインドしていなくてもnetstatに表示されます。結果として「同一ポートが systemd とアプリケーションの二つの PID に表示される」ことがあります。
このようにカーネルレベルの機構や init システムの設計により、同じポート番号が 複数の PID として列挙されることは正常な動作です。ただし、意図しないプロセスが同ポートを占有している場合は、セキュリティリスクやサービス競合の原因になるため、正しく把握・対処する必要があります。
具体的な調査手順とトラブルシューティング
以下では、netstat で同一 UDP ポートが複数 PID で表示された際の標準的な調査フローを示します。各ステップで使用するコマンド例と、よく見られる落とし穴、解決策を併せて解説します。
ステップ 1: netstat で現象を確認
Bash$ sudo netstat -lunp | grep ':53 ' udp 0 0 0.0.0.0:53 0.0.0.0:* 1234/dnsmasq udp 0 0 0.0.0.0:53 0.0.0.0:* 1/systemd
上記例ではポート 53 が dnsmasq(PID 1234)と systemd(PID 1)に表示されています。同一ポートが二つの PID で出ていることが確認できました。
ポイント
- -p オプションでプロセス情報を取得する場合、root 権限が必要です。sudo を忘れないこと。
- grep で絞り込む際は、ポート番号の前後にコロンがあることに注意(: は正規表現で特別な意味を持つためエスケープが不要です)。
ステップ 2: lsof で詳細情報を取得
netstat だけではソケットの内部属性が分かりません。lsof を併用して、SO_REUSEPORT が設定されているか確認します。
Bash$ sudo lsof -nP -i UDP:53 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME dnsmasq 1234 root 5u IPv4 12345 0t0 UDP *:53 systemd 1 root 7u IPv4 67890 0t0 UDP *:53
lsof の出力は netstat と同様ですが、-a オプションと組み合わせると ソケットオプション を確認できます。
Bash$ sudo ss -luap | grep ':53' UNCONN 0 0 *:53 *:* users:(("dnsmasq",pid=1234,fd=5),("systemd",pid=1,fd=7))
この ss の users: フィールドは、同一ポートに対して複数プロセスがバインドしている ことを明示しています。
ステップ 3: プロセスの起動方式を調査
同一ポートが systemd と他プロセスに分かれるケースは、Socket Activation が使われていることが多いです。対象サービスのユニットファイルを確認します。
Bash$ systemctl cat dnsmasq.service [Unit] Description=Dnsmasq - A lightweight DHCP and caching DNS server After=network.target [Service] ExecStart=/usr/sbin/dnsmasq -k ...
次に、socket ユニット が存在するか確認。
Bash$ systemctl list-unit-files | grep dnsmasq dnsmasq.socket enabled dnsmasq.service enabled
dnsmasq.socket が有効化されている場合、systemd がポート 53 を先にオープンし、dnsmasq が後からバインドする形になります。この構成は全く問題ありません が、意図しないプロセスがバインドしている場合は、socket ユニットを無効化し、アプリ側で直接バインドさせる必要があります。
ステップ 4: SO_REUSEPORT の有無をコードレベルで確認
自前で開発した UDP サーバが同ポートに複数インスタンスを立ち上げている場合、SO_REUSEPORT が有効になっているか確認します。C 言語の例を示します。
Cint fd = socket(AF_INET, SOCK_DGRAM, 0); int opt = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
このフラグが設定されていると、同じポート番号で複数プロセスがバインド可能 になるため、netstat に複数エントリが出ます。デバッグ時にフラグが不要であれば除去し、単一プロセスでのバインドに切り替えます。
ステップ 5: 不要なプロセスの特定と停止
調査の結果、不要なプロセス が同一ポートにバインドしていることが判明したら、対象プロセスを停止します。
Bash# 例: 予期せぬバックグラウンドプロセスがポート 12345 にバインド $ sudo kill -9 <PID> # または systemd 管理サービスの場合 $ sudo systemctl stop suspicious.service
注意点
- kill -9 は最終手段です。まずは systemctl stop や service restart で優雅に停止させましょう。
- 停止後、再度 netstat/ss でポートの状態を確認し、期待通りの PID のみが表示されているか確認します。
ハマった点やエラー解決
| 現象 | 原因 | 解決策 |
|---|---|---|
netstat で同一ポートが 3 つ以上表示される |
systemd の socket activation が重複+自前プログラムが SO_REUSEPORT を使用 |
socket ユニットを無効化、またはプログラム側で SO_REUSEPORT を外す |
ss -luap が空になる |
netstat -lunp は root 権限不足 |
sudo を忘れずに、もしくは自ユーザーが netstat の実行権限を持つように設定 |
lsof で PID が - と表示される |
カーネル内部の匿名ソケット(例えば systemd-resolved の内部) |
ss -lp で kernel-owned ソケットを確認し、必要に応じて systemd の設定を見直す |
解決策のまとめ
- 同一ポートが複数 PID に表示されても、SO_REUSEPORT や systemd の socket activation が正規の動作であることをまず確認。
- 不要なバインド が疑われる場合は
ss/lsofと組み合わせてプロセスの起動元を追跡。 - 設定変更 が必要な場合は、systemd の socket ユニットを無効化するか、アプリ側のソケットオプションを調整。
- 最終確認 は
netstat -lunpとss -luapを双方実行し、期待通りの構成になっているか検証。
まとめ
本記事では、netstat で同一 UDP ポートが複数 PID として表示される原因と、実際にトラブルを切り分ける手順を解説しました。
- 原因は
SO_REUSEPORTと systemd の socket activation が主。 - 調査手順は
netstat → ss → lsof → systemctlの流れで行う。 - 解決策は不要な socket activation の無効化、またはアプリ側の
SO_REUSEPORT設定の見直し。
これらを実践することで、ポート競合や予期せぬサービス起動 を防ぎ、システムの安定性を向上させることができます。次回は、IPv6 環境での同様の問題 と、コンテナ(Docker / Kubernetes)におけるポート共有 について掘り下げる予定です。
参考資料
- Linux man page – ss
- systemd.socket(5) – socket activation documentation
- SO_REUSEPORT – Linux Programmer's Manual
- dnsmasq – Lightweight DNS and DHCP server
- Ubuntu Server Guide – Managing services with systemd
