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コマンドを使えば、どのプロセスがポートを握っているかを調べられるので、すぐに原因にたどり着くことができるというわけだ。便利なコマンドなので、ぜひ覚えておいていただきたい。