Linuxサーバを管理していると、問題が発生した際や設定を変更する必要があるときなどに、プログラムがどのファイルを使っているのか、調べたくなるケースが少なからずある。
そんなとき、「DTrace」のようなシステム情報取得機能があれば細かく調べることができるのだが、DTraceはSolarisやmacOS、FreeBSDあたりでは使えるものの、肝心のLinuxには用意されていない。そこで使えるのが、「lsof」というコマンドだ。
DTraceのような汎用性はないが、開かれているファイルを調べるという用途であれば、このコマンドで事足りることが多い。また、Linuxを含め、比較的多くのOSで動作するので、とりあえずこれを覚えておけばよいだろう。
lsofのインストール
lsofはLinuxディストリビューションによってデフォルトでインストールされていたり、されていなかったりする。ただし、大体はパッケージとして提供されているので、各OSのパッケージ機能を使ってインストールしてもらえれば良い。インストールの流れはOSによって異なるが、次のような感じになる。
【CentOS 7の場合】
[root@centos ~]# yum install lsof
読み込んだプラグイン:fastestmirror
Repodata is over 2 weeks old. Install yum-cron? Or run: yum makecache fast
base | 3.6 kB 00:00
extras | 3.4 kB 00:00
updates | 3.4 kB 00:00
(1/2): extras/7/x86_64/primary_db | 151 kB 00:00
(2/2): updates/7/x86_64/primary_db | 4.8 MB 00:00
Loading mirror speeds from cached hostfile
* base: ftp.tsukuba.wide.ad.jp
* extras: ftp.tsukuba.wide.ad.jp
* updates: ftp.tsukuba.wide.ad.jp
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> パッケージ lsof.x86_64 0:4.87-4.el7 を インストール
--> 依存性解決を終了しました。
依存性を解決しました
================================================================================
Package アーキテクチャー バージョン リポジトリー 容量
================================================================================
インストール中:
lsof x86_64 4.87-4.el7 base 331 k
トランザクションの要約
================================================================================
インストール 1 パッケージ
総ダウンロード容量: 331 k
インストール容量: 927 k
Is this ok [y/d/N]: y
Downloading packages:
lsof-4.87-4.el7.x86_64.rpm | 331 kB 00:00
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
インストール中 : lsof-4.87-4.el7.x86_64 1/1
検証中 : lsof-4.87-4.el7.x86_64 1/1
インストール:
lsof.x86_64 0:4.87-4.el7
完了しました!
[root@centos ~]#
【macOSの場合】
/Users/daichi$ brew install lsof
==> Downloading https://homebrew.bintray.com/bottles/lsof-4.89.sierra.bottle.tar.gz
################################################ 100.0%
==> Pouring lsof-4.89.sierra.bottle.tar.gz
/usr/local/Cellar/lsof/4.89: 6 files, 286.7KB
/Users/daichi$
【FreeBSD 11】
% sudo pkg install lsof
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
The following 1 package(s) will be affected (of 0 checked):
New packages to be INSTALLED:
lsof: 4.90.i,8
Number of packages to be installed: 1
109 KiB to be downloaded.
Proceed with this action? [y/N]: y
[1/1] Fetching lsof-4.90.i,8.txz: 100% 109 KiB 111.3kB/s 00:01
Checking integrity... done (0 conflicting)
[1/1] Installing lsof-4.90.i,8...
Extracting lsof-4.90.i,8: 100%
%
lsofコマンドを何の引数も指定しないで実行すると、全てのプロセスに関して開いている情報を表示してくれる。基本的には、この出力を見れば欲しい情報は得られるのではないかと思う。
だが、lsofはオプションが多いコマンドでもあり、オプションを指定することでさまざまな視点から情報をまとめることができる。例えば、指定したプロセスについてのみ情報を表示させたい場合には次のように-pオプションにプロセスIDを指定して実行すればよい。
% lsof -p 600
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 600 root cwd VDIR 0,90 512 2 /
nginx 600 root rtd VDIR 0,90 512 2 /
nginx 600 root txt VREG 0,90 913232 2086907 /usr/local/sbin/nginx
nginx 600 root txt VREG 0,90 136760 3290906 /libexec/ld-elf.so.1
nginx 600 root txt VREG 0,90 118352 240821 /lib/libthr.so.3
nginx 600 root txt VREG 0,90 57904 240788 /lib/libcrypt.so.5
nginx 600 root txt VREG 0,90 491280 2332993 /usr/local/lib/libpcre.so.1.2.8
nginx 600 root txt VREG 0,90 459504 1926302 /usr/lib/libssl.so.8
nginx 600 root txt VREG 0,90 2529000 240770 /lib/libcrypto.so.8
nginx 600 root txt VREG 0,90 95072 240830 /lib/libz.so.6
nginx 600 root txt VREG 0,90 1744432 240771 /lib/libc.so.7
nginx 600 root 0u VCHR 0,26 0t0 26 /dev/null
nginx 600 root 1u VCHR 0,26 0t0 26 /dev/null
nginx 600 root 2w VREG 0,90 108326 2167621 / (/dev/da0p2)
nginx 600 root 3u unix 0xfffff80003a8f000 0t0 ->0xfffff80003bfaa20
nginx 600 root 4w VREG 0,90 3425540 2167622 / (/dev/da0p2)
nginx 600 root 5w VREG 0,90 108326 2167621 / (/dev/da0p2)
nginx 600 root 6u IPv4 0xfffff80003d2f820 0t0 TCP *:http (LISTEN)
nginx 600 root 7u unix 0xfffff80003bfaa20 0t0 ->0xfffff80003a8f000
%
また、lsofコマンドではプロセスが開いているポート番号も表示させることができる。サーバを運用していると、既にポートが使われているためにサーバプロセスを起動することができないといったことがある。そういった場合に、lsofコマンドを使うとどのプロセスがポートを開いているかを調べることができるのだ。
例えば、Apache HTTPサーバが何らかの原因でカーネルによって終了させられたケースを考えてみよう。ほかのプログラムにメモリリークがあったとか、データを保持しすぎたといった理由でスワップ領域を使い尽くし、その結果としてまるで関係ないApache HTTPサーバがカーネルによって強制終了させられたとする。
この場合、問題となったプログラムを終了した後にApache HTTPサーバを起動すればよいわけだが、カーネルが強制終了したプロセスがApache HTTPサーバのある1つのプロセスのみで、ほかのプロセスは残ったままになっていることがある。すると、残ったプロセスが既にポートを使っているので、新しくApache HTTPサーバを起動することができない。
そこでlsofコマンドを使えば、どのプロセスがポートを握っているかを調べられるので、すぐに原因にたどり着くことができるというわけだ。便利なコマンドなので、ぜひ覚えておいていただきたい。