カーネルモジュールとデバイス管理

はじめに

  • 個人的なLinuxに関する勉強メモです。想像の部分も多々あり。また間違いもあると思います。この情報を利用する際は、完全自己責任でお願いします。

環境

  • CentOS release 6.5 (Final)

カーネルモジュールとは?

  • カーネルの機能をモジュールとして外部ファイルにしたもの。
  • ハードウェアを扱う為のドライバ、ファイルシステムやTCP/IP機能を利用する為のドライバ等があり。
  • 必要に応じてカーネルに組み込んだり、取り外したりできる。
  • モジュールの格納先は/lib/modules/`uname -r`ディレクトリ
  • ファイルの拡張子は.ko
  • 以下のメリットがあり。
    • カーネルイメージのファイルサイズを小さくできる。
    • サードパーティによるモジュール追加時にカーネルコンパイルが不要

カーネルモジュールを管理するコマンド

  • 以下のコマンドでカーネルにモジュールを追加したり、取り外したりできる。
    • depmod
    • insmod
    • lsmod
    • modinfo
    • modprobe
    • rmmod

カーネルモジュールの自動読み込み処理

  • カーネルモジュールの種類として、大きく2つある。
    • ①EtehrnetコントローラやSCSIデバイス等、ハードウェアデバイスを制御する機能を提供するモジュール(デバイスドライバ)
    • ②ファイルシステムやTCP/IP通信を行う為の機能を提供するモジュール
  • ①のようなモジュール(デバイスドライバ)は、主にudevによりシステム起動時、またデバイスの接続時に自動的に読み込まれている。
  • ②のようなモジュールは、それらの機能を利用する時に自動的に読み込まれている。
  • これらの自動読み込みの機能があるおかげで、いちいちmodprobeコマンド等を実行してモジュールをロードする機会は少ない。(個人的経験では。。。)

そもそも自動読み込み処理ってどうやってるの?

  • 自動読み込みを手動でやる場合を想定してみる。そうすると以下の問題に気付く。
    • 利用したいデバイスに対して必要なモジュールは何なのか?
      • 例)IntelのGigabit Ethernetコントローラを利用する為には、どのモジュールをロードすればいいの?
    • 利用したい機能に対して必要なモジュールは何なのか?
      • 例)ext2でフォーマットされたディスクをマウントしてデータを参照する為には、ext2のファイルシステムを利用する為の機能が必要だが、どのモジュールをロードすればいいのか?

利用したいデバイスに対して必要なモジュールは何なのか?の答え。

  • ハードウェアはmodalias情報をそれぞれもっており、その情報は接続時にカーネルに通知されている。カーネルは検知したデバイスのmoalias情報をudevに通知する。認識しているデバイスのmodalias情報の確認は/sysディレクトリ配下にファイル名前がmodaliasにて格納されている為、以下コマンドにて可能。
    # find /sys/ -name "modalias" -exec cat {} \;
  • udevはuserspace device managementの略。デバイスの管理デーモンで、kernelからデバイスの接続、切断の通知を受けて、必要な処理(デバイスファイルの生成、モジュールのロード等)をする。詳しくはこちら
  • デバイスドライバ(モジュール)の中にハードコーディングで対応するデバイスの情報(modalias)が格納されている。確認は雑だとけど、以下コマンドで可能。
    # egrep -a alias /lib/modules/2.6.32-431.el6.x86_64/kernel/drivers/net/e1000/e1000.ko
  • 各デバイスドライバ(モジュール)の中に含まれるmodalias情報はdepmod -aコマンドの実行により、対応情報が、/lib/modules/`uname -r`/modules.aliasに反映される。(その為、手動でドライバを追加した場合は、depmodコマンドを実行する必要があり。)
    例)# grep "alias.*e1000$" /lib/modules/`uname -r`/modules.alias | head -1
    alias pci:v00008086d00002E6Esv*sd*bc*sc*i* e1000
    デバイスのmodaliasが"pci:v00008086d00002E6Esv*sd*bc*sc*i*"にマッチしたら、e1000モジュールを使え!ってこと。*は複数文字とマッチする。
  • これらの情報から、デバイスに対する必要なデバイスドライバ(モジュール)を特定している。
  • ちなみに、自分の検証環境では、Etherntコントローラはe1000モジュールによって動作していて、デバイスとモジュールの対応付けは以下で行われている。
    • デバイスのmodaliasは以下の通り。
      # cat /sys/devices/pci0000:00/0000:00:11.0/0000:02:01.0/modalias
      pci:v00008086d0000100Fsv000015ADsd00000750bc02sc00i00
    • e1000モジュールが対応するmodalias情報は以下の通り
      # egrep "e1000$" /lib/modules/2.6.32-431.el6.x86_64/modules.alias |egrep ":v00008086d0000100F"
      alias pci:v00008086d0000100Fsv*sd*bc*sc*i* e1000
    • このルールがマッチした為、e1000が適用されている事が分かる。
    • lspci -n -nn -vのコマンドで出力結果でも、Ethternetコントローラに対して動作しているモジュールが分かる。(" Kernel driver in use"を参照)

デバイスに対して必要なモジュールの自動読み込みはどのように行われているのか?

  • カーネルがデバイスを検知すると、udevに通知をおくる。udevはカーネルから受けたデバイスに関する情報(moaliases)を利用して、必要な処理をする。(/lib/udev/rules.d配下、または/etc/udev/rules.d配下の.rulesファイルの内容に従って、デバイスファイルの生成、modprobeコマンドの実行をする。)
    /lib/udev/rules.d/80-drivers.rulesファイルの以下行を参照。
    DRIVER!="?*", ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe -b $env{MODALIAS}"
  • modprobeはmodules.aliasまた、/etc/modprobe.d配下のalias定義を利用して、対応するモジュールを読み込む。
  • modprobeの設定ファイルに記載されているalias定義には2種類あり。
    • 識別情報に対して別名として対応モジュールを定義する場合
      カーネルがモジュールを読み込む時に、識別情報を利用する。利用したい機能やデバイスファイルを識別情報で表現している。例えばカーネルがSCSIブロックデバイス(メージャー番号8)の第1パーティションにアクセスしたい!って思った時に、
      # modprobe sd_mod(SCSIブロックデバイス用カーネルモジュール)ではなく、# modprobe block-major-8-1(識別情報)
      で、モジュールの読み込みが可能。この方が分かりやすく、処理の可読性は増す。これを実現すべくblock-major-8-1 = sd_modの対応付けをしているのが、識別情報のalias。例としては、"alias net-pf-1 unix"(net-pf-ソケット種別)や"alias block-major-8-* sd_mod"(デバイス種別-major-メジャー番号-マイナー番号)
      デバイスファイルのメジャー番号とデバイスの対応は以下に定義があり。
      /usr/share/doc/kernel-doc-2.6.32/Documentation/devices.txt
      /proc/devices
    • modaliasに対して別名として対応モジュールを定義する場合
      例)alias pci:v00001022d00002000sv*sd*bc*sc*i* vmxnet
  • udevに通知される情報、またそれに対応するudevの動作を調べる方法は以下があり。
    • 以下コマンドの実行
      # udevadm -d monitor --kernel --property --udev
      上記コマンドを実行し、デバイスの接続、切断をするとudevに通知された情報、udevの対応動作を確認できる。ただ、この方法だとシステム起動時の動作は分からない。起動しないとudevadmコマンドがたたけないから。。。
    • システム起動時のudevの動作を調べるには、以下方法があり。
      /sysディレクトリ配下のueventファイルに適当なデータを入力すると、kernelがudevにデバイス情報を通知する動きがあり。
      例)echo "" > /sys/devices/pci0000:00/0000:00:10.0/host2/target2:0:0/2:0:0:0/block/sda/uevent
      udevadmを実行し、上記のようにueventファイルにデータを入力すると、kernel→udevへの通知が送られるみたい。
  • /sysディレクトリはカーネルが認識している情報が格納されている。デバイスのmodalias情報は、modaliasファイルに記載があり。
    例)# find /sys -name "modalias" -exec cat {} \;で参照可能。
  • 要は、Kernelがデバイス検知→udevがデバイスファイル生成、modprobeコマンドを使って対応モジュールのロードをする事で自動読み込みが行われている。

利用したい機能に対して必要なモジュールは何なのか?の答え

  • udevではなく機能を利用する際に実行したコマンドから呼び出されるシステムコール内で対応するモジュールが直接指定されているのでは?と思われる。
  • 検証結果
    # lsmod |egrep ext2
    # mke2fs /dev/sdb
    # lsmod |egrep ext2
    # mount -t ext2 /dev/sdb /mnt
    # lsmod |egrep ext2
    ext2 68236 1
    mbcache 8193 3 ext2,ext3,ext4
    #

補足メモ

  • デバイスファイルがあれば、そこから対応モジュールを特定して自動読み込みする事も可能。
    • # ls -la /dev/sr0 (デバイスファイルの確認)
      brw-rw---- 1 root cdrom 11, 0 5月 4 21:57 2014 /dev/sr0
      # lsmod |egrep sr_mod (ロードされているモジュールの確認)
      sr_mod 15177 0
      cdrom 39085 1 sr_mod
      # modprobe -r sr_mod (SCSI CD-ROM devices用のモジュールをアンロード)
      # vi /etc/modprobe/dist.conf
      変更前
      alias block-major-11-* sr_mod
      変更後
      alias block-major-11-* cbc (ロードされていない適当なモジュールに変更)
      # mknod /dev/sr0 b 11 0 (sr_modをアンロードした時に削除されている為、作成)
      # lsmod | egrep cbc (まだcbcはロードされていないことを確認)
      # mount /dev/sr0 /mnt (モジュールが違う為エラー終了)
      mount: you must specify the filesystem type
      # lsmod | egrep cbc (modprobe block-major-11-0が実行された事を確認。ただ0であることから利用されていない。)
      cbc 3083 0
      # vi /etc/modprobe/dist.conf (正しいモジュールに戻す)
      変更前
      alias block-major-11-* cbc
      変更後
      alias block-major-11-* sr_mod
      # mount /dev/sr0 /mnt (戻したら正常にモジュールが読み込まれ動作することを確認。)
      mount: block device /dev/sr0 is write-protected, mounting read-only
      #
      ※mountコマンドにstraceを実行してもmodprobeコマンドが実行された事は確認できなかった。。。ただ、動作としては、このタイミングでcbcがロードされているから、動作としてはそうなのだろう。cbcモジュールがロードされても、もちろん利用されていない。利用されるかは、やはりドライバ内に記載されている対応機器とマッチしないと駄目なのだろう。要は、modprobeコマンドはあくまでモジュールをロードしているだけで、ある任意のデバイスに対して、特定のモジュールを利用するよう指示するコマンドではないとの事。
  • "ある任意のデバイスに対して、特定のモジュールを利用するよう指示する"方法として、"ダイナミックデバイスID"という仕組みがあるよう。ただ、PCI/PCI-Express、USB接続機器のみ利用可能。利用方法は以下の通り。
    • 例)モジュールがHOGE_MODULEで、USB接続機器HOGE_DEVICE(VendorIDはXXXX、ProductIDはYYYY)を制御したい場合は以下オペレーションを実行する。
      # modprobe HOGE_MODULE (モジュールをロード)
      # echo "XXXX YYYY" > /sys/bus/usb/drivers/HOGE_MODULE/new_id
      上記オペレーションにて、特定のデバイスに対して利用するドライバを指定することが可能。
    • 上記では、システム再起動すると無効になる。有効にするには、/etc/modprobe.d配下にファイル(HOGE_MODULE.conf)を作成して、以下の記述をする必要がある。
      alias usb:vXXXXpYYYYd*dc*dsc*dp*ic*isc*ip* HOGE_MODULE
      install HOGE_MODULE /sbin/modprobe --ignore-install HOGE_MODULE $CMDLINE_OPTS && /bin/echo "XXXX YYYY" > /sys/bus/usb/drivers/HOGE_MODULE/new_id
      aliasの"vXXXXpYYYYd"以外の部分が持つ意味の詳細は未調査。ここは極力*を使わないと接続場所が変わった場合に、マッチしなくなったりするらしい。まあ該当デバイスファイル(HOGE_DEVICEのmodalias情報を調べて、VendorID・ProductIDの部分を変更し、他の部分は適宜*に置き換えればいいのだろう。)

デバイス管理で使えるコマンド

  • lspci -n -nn -v
  • lsusb -v
  • lsscsi -lll -v
  • lshw -numeric

参考資料


  • 最終更新:2014-05-06 20:00:30

このWIKIを編集するにはパスワード入力が必要です

認証パスワード