私が開発しているLinuxカーネルドライバーの1つは、カーネルでネットワーク通信を使用しています(、、sock_create()
などsock->ops->bind()
)。
問題は、データを受信するための複数のソケットがあることです。したがって、カーネル空間でselect()
またはをシミュレートするものが必要です。poll()
これらの関数はファイル記述子を使用するため、システムコールを使用してソケットを作成しない限り、システムコールを使用することはできませんが、カーネルで作業しているため、これは不要のようです。
sock->sk_data_ready
そのため、デフォルトのハンドラーを自分のハンドラー( )でラップすることを考えていましcustom_sk_data_ready()
た。これにより、セマフォのロックが解除されます。次にkernel_select()
、セマフォをロックしようとし、セマフォが開くまでブロッキング待機を行う独自の関数を作成できます。このようにして、セマフォがによってロック解除されるまで、カーネル関数はスリープ状態になりますcustom_sk_data_ready()
。ロックを取得するとkernel_select()
、ロックが解除され、再度ロックするようcustom_sk_data_ready()
に呼び出されます。したがって、追加の初期化はcustom_sk_data_ready()
、ソケットをバインドする前に実行することだけです。これにより、への最初の呼び出しがcustom_select()
誤ってトリガーされなくなります。
考えられる問題が1つあります。複数の受信が発生した場合、への複数の呼び出しcustom_sk_data_ready()
はセマフォのロックを解除しようとします。したがって、複数の呼び出しを失わず、使用されているものを追跡するsock
には、使用されているソケットへのポインターのテーブルまたはリストが必要になります。そしてcustom_sk_data_ready()
、渡されたソケットのテーブル/リストにフラグを立てる必要があります。
この方法は正しいですか?または、標準のシステムコールを使用するときに、ユーザー/カーネルスペースの問題に苦労する必要がありますか?
最初の発見:
構造内のすべてのコールバック関数はsock
、割り込みコンテキストで呼び出されます。これは彼らが眠ることができないことを意味します。メインカーネルスレッドが準備完了ソケットのリストでスリープできるようにするには、ミューテックスが使用されcustom_sk_data_ready()
ますが、ミューテックスのスピンロックのように機能する必要があります(mutex_trylock()
繰り返し呼び出す)。GFP_ATOMIC
これは、動的割り当てでフラグを使用する必要があることも意味します。
追加の可能性:
開いているソケットごとに、各ソケットsk_data_ready()
をカスタムソケット()に置き換えcustom_sk_data_ready()
、ワーカー(struct work_struct
)とワークキュー(struct workqueue_struct
)を作成します。process_msg()
ワーカーごとに共通の機能を使用します。カーネルモジュールレベルのグローバルリストを作成します。各リスト要素にはソケットへのポインターがあり、ワーカー構造が含まれています。ソケットでデータの準備ができるcustom_sk_data_ready()
と、同じソケットで一致するリスト要素を実行して検索queue_work()
し、リスト要素のワークキューとワーカーを使用して呼び出します。次に、関数が呼び出され、パラメーターprocess_msg()
の内容(アドレス)から一致するリスト要素を見つけるか、マクロを使用してワーカー構造を保持するリスト構造のアドレスを取得できます。struct work_struct *
container_of()
どのテクニックが最も健全ですか?