NetBSD Advent Calendar 2021 10日目の記事です。今日はロードされているカーネルモジュールの依存関係を可視化する話をしようと思います。
背景
NetBSDに限らず、現代のOSでは「カーネルモジュール」という形でOSを稼働させたまま特定の機能を追加・削除できます。例えばNetBSDでは、modstat(8)コマンドでロードされているカーネルモジュールを確認できます。
$ modstat
NAME CLASS SOURCE FLAG REFS SIZE REQUIRES
aac driver builtin - 1 - pci
accf_dataready misc builtin - 0 - -
accf_httpready misc builtin - 0 - -
acpiacad driver builtin - 0 - sysmon_envsys,sysmon_power
acpibat driver builtin - 0 - sysmon_envsys
acpibut driver builtin - 0 - sysmon_power
...
NetBSDのデフォルトインストール状態だと、約260個ほどのカーネルモジュールがロードされているようです。中にはサウンド系のデバイスなど、私の環境では使用しない(※)モジュールもあるようです。
(※VirtualBox上でサウンドデバイスを無効化しているので…)
$ modstat | wc -l
265
というワケで、不要なカーネルモジュールを除去したいのですが、モジュールには依存関係があるようで、ちゃんと順番を考えないとエラーになってしまいます。
$ sudo modunload compat_linux
modunload: compat_linux: Device busy
modstat
コマンドの REQUIRES
フィールドには、該当のカーネルモジュールを必要としている(=依存関係のある)モジュールが記載されているようです。この情報をパースすれば、カーネルモジュールの依存図が作成できそうです。
$ modstat | egrep 'NAME|compat_linux'
NAME CLASS SOURCE FLAG REFS SIZE REQUIRES
compat_linux exec builtin - 1 - compat_ossaudio,sysv_ipc,compat_util,compat_50,compat_43,exec_elf64,exec_aout
compat_linux32 exec builtin - 0 - compat_linux,sysv_ipc,compat_sysv_50,compat_netbsd32_50,compat_netbsd32_43,exec_elf32,compat_netbsd32,compat_netbsd32_sysvipc
カーネルモジュールの依存関係を可視化する
modstat
をパースする簡単なスクリプトを作成してみます。
#!/usr/bin/env ruby
kmod_list = {}
IO.popen('modstat', 'r').readlines.each_with_index do |record, index|
next if index == 0
name, _, _, _, _, _, requires = record.chomp.gsub(/ +/, ' ').split(' ')
requires.split(',').each do |kmod|
kmod_list[name] = kmod
end
end
puts 'digraph "mygraph" {'
kmod_list.each_pair do |kmod, req|
next if req == '-'
puts " \"#{kmod}\" -> \"#{req}\";"
end
puts '}'
modstat
コマンドにおける、NAMEとREQUIRESの部分をグラフ化するだけです。
$ ./sample.rb > tmp.dot
digraph "mygraph" {
"aac" -> "pci";
"acpiacad" -> "sysmon_power";
"acpibat" -> "sysmon_envsys";
...
"vmbus" -> "hyperv";
"vmt" -> "sysmon_taskq";
"vnd" -> "zlib";
"wmidell" -> "sysmon_power";
"wmieeepc" -> "sysmon_power";
"wmihp" -> "sysmon_envsys";
"wmimsi" -> "acpiwmi";
"xc3028" -> "i2cexec";
"xc5k" -> "i2cexec";
}
Graphvizでグラフ化します。
$ dot -Tsvg tmp.dot > output.svg
こんな感じで可視化できました。こうやって見ると、いくつかのモジュール群になっているのがわかりますね。
画像だと表示がつぶれてしまうので、SVG形式にしたものをGistに用意しました。
まとめ
modstat
の出力から、カーネルモジュールの依存関係を可視化してみました。モジュール名から明らかに自分では使用していないものが判別できたりするので、不要なモジュールを除去することで、よりスリムな(?)カーネルにできそうです。