前回は,カーネルにモジュールを追加する処理をデバイス・ドライバを例に解説しました。デバイスに依存しない上位層の共通関数と,デバイスに依存する下位層の実関数を結び付けているのが構造体でした。今回は,下位に共通関数,上位に実関数がくる場合をネットワーク処理を例に見ていきます。

 前回は,カーネルでよく使われている構造体の利用方法を紹介しました。デバイスに依存する処理を記述した「実関数」と,どのデバイスでも共通に使える「共通関数」を別々に定義しておき,構造体を使って,実関数と共通関数を同じものとして定義するというものでした。これにより,いかなるデバイスを使うときでも同じ共通関数名が使えるようになり,デバイスを物理的に差し替えたときも,上位層のプログラムのソース・コードを書き換えずに済みます。この方法はデバイス・ドライバだけではなく,カーネルのモジュール一般でよく用いられています。

 今回も,同じように上位層と下位層を構造体で結び付ける方法について見ていきます。ただし,前回とは逆に,下位層が共通関数となり,上位層が実関数になります。

 その代表例が,ネットワークの処理プログラムです。パケットの受信処理は,下位層から上位層に向かいます。同一回線上を流れる複数種類の情報を処理するために,下位層で共通の関数を使い,上位層で各情報を処理する実関数に分岐するという具合になります。

 Linuxでは通常,IP(internet protocol)という通信規約を用いて他のコンピュータと通信します。IPは,IPアドレスやパケットのフォーマットを記述しています。IPにはバージョンがあり,今普及しているのが「IPv4」といわれるバージョン4のものです。IPには,「IPv6」というバージョン6のものもあり,一部で使われ始めています。

 IPv4のパケットもIPv6のパケットも同じ回線上を流れます。しかし,実際にこれらのパケットを処理するカーネル関数は別です。 IPv4パケットを受信したならip_rcv()関数が,IPv6パケットならipv6_rcv()関数が処理します。

 しかし,カーネル内部では,ネットワークからパケットを受け取ったときに処理する関数名として,


ret = pt_prev->func();

という1つの共通関数名を使っています。この関数は,その実体がIPv6かIPv4なのか分からないので,これ以降の処理関数名を特定できず,カーネル・リーダー泣かせとなっています。そこで,構造体が登場します。