ほとんどのデバイス ドライバーのすべての関数が静的であるのはなぜですか? 静的関数はファイル スコープの外では見えないためです。では、これらのドライバー関数はユーザー空間アプリケーションによってどのように呼び出されるのでしょうか?
3 に答える
C ではすべてがアドレスであることを忘れないでください。つまり、アドレスがあれば関数を呼び出すことができます。EXPORT_SYMBOL
カーネルには、まさにそれを行う名前のマクロがあります。関数のアドレスをエクスポートして、ヘッダー宣言を配置することなくドライバー関数を呼び出すことができるようにします。これらの関数はコンパイル時に認識されない場合があるためです。このような場合、静的修飾子は、ドライバー コードを含む可能性のある他のファイルからではなく、このメソッドを介してのみ呼び出されるようにするために作成されます (場合によっては、ドライバー コード ヘッダーを含めて直接呼び出すことはお勧めできません)。 .
編集:ユーザー空間をカバーしていないことが指摘されたので。
ドライバー関数は通常、ユーザー空間を介して直接呼び出されることはありません (コンテキスト スイッチを保存するためにいくつかの小さなトリックを実行する SYSCALL 命令の x86 実装を除く)。したがって、ここで static キーワードを使用しても違いはありません。カーネル空間にのみ違いがあります。@Cong Wang が指摘したように、関数は通常、関数ポインターの構造体に配置されるため、構造体がこの構造体を指すようにするだけで呼び出すことができます (file_ops、スケジューラー、ファイルシステム、ネットワーク コードなど)。
これらの静的関数は、モジュールの外部で直接使用することを想定していないためです。それらは、モジュール内の他の関数によって呼び出されます。その中には、ioctl へのインターフェイスやその他のコールバックがあります。これが、ユーザー空間から呼び出すことができる理由です。呼び出しパスにあるだけです。
ネットワーク ダミー モジュールを見てみましょう。
dummy_dev_init() は明らかに静的です:
static int dummy_dev_init(struct net_device *dev)
{
dev->dstats = alloc_percpu(struct pcpu_dstats);
if (!dev->dstats)
return -ENOMEM;
return 0;
}
ただし、このネットワーク デバイスを登録するときに呼び出される ->ndo_init() のコールバックです。
static const struct net_device_ops dummy_netdev_ops = {
.ndo_init = dummy_dev_init,
.ndo_uninit = dummy_dev_uninit,
.ndo_start_xmit = dummy_xmit,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_rx_mode = set_multicast_list,
.ndo_set_mac_address = eth_mac_addr,
.ndo_get_stats64 = dummy_get_stats64,
.ndo_change_carrier = dummy_change_carrier,
};
そして、当然のことながら、dummy_dev_init() を直接呼び出すべきではありません。
カーネルには何千ものモジュールがあり、それらは (または以前は) すべてオブジェクト ファイルであり、リンクと同様のプロセスを介して動的にロードされます (または実際にリンクされています)。指定されていない限り、デフォルトの C の動作のように、すべての関数名をすべてエクスポートするとしたら、名前の競合がどれだけ発生するか想像できますstatic
か?
ユーザー空間アプリケーションはドライバー関数を直接呼び出すことはできませんが、対話する方法は他にもあります。