ホームページ  >  記事  >  システムチュートリアル  >  ユーザー空間とカーネル空間間の通信に Netlink を使用する

ユーザー空間とカーネル空間間の通信に Netlink を使用する

王林
王林転載
2024-01-16 08:48:05889ブラウズ

用户空间和内核空间通讯-Netlink 上

2001 年、ForCES IETF 委員会は Netlink の標準化作業を正式に実施しました。 Jamal Hadi Salim は、Netlink を、ネットワーク デバイスのルーティング エンジン コンポーネントとその制御および管理コンポーネント間の通信用のプロトコルとして定義することを提案しました。しかし、彼の提案は最終的には採用されず、今日私たちが見ているパターン、すなわち Netlink が新しいプロトコル ドメイン、ドメインとして設計されたものに置き換えられました。

Linux の父である Tobas はかつてこう言いました。「Linux は進化であり、インテリジェントなデザインではありません。」どのような意味です?言い換えれば、Netlink も Linux の特定の設計概念に従っています。つまり、完全な仕様書や設計書は存在しません。いったい何?ご存知の通り、「ソースコードを読んでください」。

もちろん、この記事は Linux 上の Netlink の実装メカニズムを分析することではなく、「Netlink とは何か」と「Netlink を上手に活用する方法」というトピックを共有することを目的としています。問題が発生した場合は、ソース コードで原因を調べてください。

ネットリンクとは

Netlink を理解するには、いくつかの重要なポイントを把握する必要があります。

1. データグラム指向のコネクションレス型メッセージング サブシステム

2. 一般的な BSD ソケット アーキテクチャに基づいて実装されています

最初の点については、UDP プロトコルを考えるのが簡単で、これを考えるのは素晴らしいことです。 UDP プロトコルに基づいた Netlink を理解することは無理ではなく、類似点を引いて類推して学び、要約と関連付けが得意になり、最終的に知識の伝達が実現できれば、それが学習の本質です。 Netlink は、カーネル -> ユーザー間、およびユーザー -> カーネル間の双方向かつ非同期データ通信を実現でき、2 つのユーザー プロセス間、さらには 2 つのカーネル サブシステム間のデータ通信もサポートします。この記事では、後者 2 つは考慮せず、ユーザー <-> カーネル間のデータ通信を実装する方法に焦点を当てます。

2 番目のポイントを見たとき、次の絵が頭に浮かびましたか?もしそうなら、あなたには知恵の根があるということですが、もちろん、そうでなくても、知恵の根はゆっくり成長するので問題ありません(笑)。

後で Netlink ソケット プログラミングを練習するときは、主にソケット()、バインド()、sendmsg()

を使用します。

や recvmsg() などのシステム コール、およびもちろんソケットによって提供されるポーリング メカニズム。

ネットリンク通信タイプ

Netlink は、ユニキャストとマルチキャストの 2 種類の通信方式をサポートします。

ユニキャスト: ユーザー プロセスとカーネル サブシステム間の 1:1 データ通信によく使用されます。ユーザー空間はカーネルにコマンドを送信し、カーネルからコマンドの結果を受け取ります。

マルチキャスト: カーネル プロセスと複数のユーザー プロセス間の 1:N データ通信によく使用されます。カーネルはセッションの開始側として機能し、ユーザー空間アプリケーションは受信側になります。この機能を実装するために、カーネル空間プログラムはマルチキャスト グループを作成し、カーネル プロセスによって送信されたメッセージに関心のあるすべてのユーザー空間プロセスがそのグループに参加して、カーネルから送信されたメッセージを受信します。次のように:###

用户空间和内核空间通讯-Netlink 上

プロセス A とサブシステム 1 の間の通信はユニキャストであり、プロセス B および C とサブシステム 2 の間の通信はマルチキャストです。上の写真からもメッセージが伝わってきます。ユーザー空間からカーネルに転送されるデータはキューに入れる必要がなく、つまり操作は同期的に完了しますが、カーネル空間からユーザー空間に転送されるデータはキューに入れる必要があり、これは非同期です。これを理解すると、Netlink に基づいてアプリケーション モジュールを開発する際に、多くの回り道を省くことができます。カーネルにメッセージを送信し、ルーティング テーブルやその他の情報など、カーネル内の特定の情報を取得する必要がある場合、ルーティング テーブルが大きすぎる場合、カーネルが Netlink 経由でデータを返すときに、次のことを考えることができます。データの問題は、結局のところ、出力キューを見た後は、見て見ぬふりをすることはできません。

ネットリンクメッセージ形式 Netlink メッセージは、メッセージ ヘッダーとペイロードの 2 つの部分で構成されます。Netlink メッセージ全体は 4 バイトにアライメントされており、通常はホストのバイト オーダーで送信されます。メッセージ ヘッダーは 16 バイトに固定されており、メッセージ本文の長さは可変です:

用户空间和内核空间通讯-Netlink 上

ネットリンクメッセージヘッダー メッセージ ヘッダーは ファイルで定義され、構造 nlmsghdr:

で表されます。

(ここ)をクリックして折りたたむか開く

    構造体nlmsghdr
  1. {
  2. __u32 nlmsg_len; /* ヘッダーを含むメッセージの長さ */
  3. __u16 nlmsg_type; /* メッセージの内容 */
  4. __u16 nlmsg_flags; /* 追加のフラグ */
  5. __u32 nlmsg_seq; /* シーケンス番号 */
  6. __u32 nlmsg_pid; /* 送信プロセス PID */
  7. };
メッセージヘッダーの各メンバー属性の説明と説明:

nlmsg_len: メッセージ全体の長さ (バイト単位で計算)。 Netlink メッセージ ヘッダー自体が含まれます。

nlmsg_type: メッセージのタイプ、つまり、データか制御メッセージか。現在 (カーネル バージョン 2.6.21) Netlink は、次の 4 種類の制御メッセージのみをサポートしています。

NLMSG_NOOP - 空のメッセージ、何もしません;

NLMSG_ERROR - メッセージにエラーが含まれていることを示します。

NLMSG_DONE - カーネルが Netlink キューを通じて複数のメッセージを返す場合、キュー内の最後のメッセージのタイプは NLMSG_DONE で、残りのすべてのメッセージの nlmsg_flags 属性には NLM_F_MULTI ビットが有効に設定されています。

NLMSG_OVERRUN - まだ使用されていません。

nlmsg_flags: メッセージに添付される追加の説明情報 (上記の NLM_F_MULTI など)。抜粋は次のとおりです:

nlmsg_flags には複数の値があることがわかっていれば、それぞれの値の役割と意味については、Google やソース コードを参照すれば必ず答えが見つかるので、ここでは説明しません。以前の 2.6.21 カーネルのすべての値:

用户空间和内核空间通讯-Netlink 上

nlmsg_seq: メッセージのシーケンス番号。 Netlink はデータグラムを指向しているため、データ損失のリスクがありますが、Netlink はメッセージが失われないことを保証するメカニズムを提供し、プログラム開発者が実際のニーズに応じて実装できるようにします。メッセージ シーケンス番号は通常、NLM_F_ACK タイプのメッセージと組み合わせて使用​​されます。ユーザーのアプリケーションが、送信するすべてのメッセージがカーネルによって正常に受信されることを確認する必要がある場合は、メッセージの送信時にユーザー プログラム自体がシーケンス番号を設定する必要があります。カーネルはメッセージを受信し、シリアル番号を抽出し、ユーザプログラムに送信する応答メッセージに同じシリアル番号を設定します。 TCP の応答および確認メカニズムにある程度似ています。

注: カーネルがブロードキャスト メッセージをユーザー空間にアクティブに送信する場合、メッセージ内のこのフィールドは常に 0 になります。

nlmsg_pid: ユーザー空間プロセスとカーネル空間内の特定のサブシステムの間で Netlink を介してデータ交換チャネルが確立されると、Netlink はそのような各チャネルに一意のデジタル ID を割り当てます。その主な機能は、ユーザー空間からの要求メッセージと応答メッセージを関連付けることです。率直に言うと、ユーザー空間に複数のユーザー プロセスがあり、カーネル空間に複数のプロセスがある場合、Netlink は、「ユーザーとカーネル」空間の通信プロセスの各ペア間のデータ対話が一貫していることを保証するメカニズムを提供する必要があり、障害が発生する可能性があります。

用户空间和内核空间通讯-Netlink 上

つまり、プロセス A と B が Netlink を通じてサブシステム 1 から情報を取得する場合、サブシステム 1 は、プロセス A に送り返された応答データがプロセス B に送信されないことを保証する必要があります。これは主に、ユーザー空間プロセスがカーネル空間からデータを取得するシナリオに適しています。通常、ユーザー空間プロセスがカーネルにメッセージを送信するとき、システム コール getpid() を通じて現在のプロセスのプロセス ID をこの変数に割り当てます。つまり、ユーザー空間プロセスは、必要なときにこれを実行します。カーネルからの応答を取得します。カーネルからユーザー空間にアクティブに送信されるメッセージの場合、このフィールドは 0 に設定されます。

ネットリンクメッセージ本文 Netlink のメッセージ本文は TLV (Type-Length-Value) 形式を採用しています:

用户空间和内核空间通讯-Netlink 上

Netlink の各属性は、 ファイル内の

struct nlattr{} によって表されます:

用户空间和内核空间通讯-Netlink 上

Netlink によって提供されるエラー通知メッセージ ###コンテンツ###

Netlink を介したユーザー空間アプリケーションとカーネル空間プロセス間の通信中にエラーが発生した場合、Netlink はこのエラーをユーザー空間に通知する必要があります。 Netlink はエラー メッセージを個別にカプセル化します (: ) (ここ)をクリックして折りたたむか開く

構造体nlmsgerr

    {
  1. int error; //標準エラー コード。errno.h ヘッダー ファイルで定義されます。 perror() を使用して
  2. を説明できます
  3. struct nlmsghdr msg; //どのメッセージが構造体のエラー値をトリガーしたかを示します
  4. };
  5. Netlink プログラミングで注意が必要な問題
Netlink ユーザーとカーネルの通信に基づいて、パケット損失が発生する可能性のある状況が 2 つあります。 1. メモリが使い果たされました;

2. ユーザー空間受信処理のバッファオーバーフロー。バッファ オーバーフローの主な理由は、ユーザー空間プロセスの実行が遅すぎること、または受信キューが短すぎることです。

Netlink がユーザー空間の受信プロセスにメッセージを正しく配信できない場合、ユーザー空間の受信プロセスは、recvmsg() システム コールを呼び出すときにメモリ不足 (ENOBUFS) エラーを返します。これには注意が必要です。つまり、ユーザー→カーネルからのsendmsg()システムコールではバッファオーバーフローの状況が送信されないことになりますが、理由は前述したとおりですので、ご自身で考えてみてください。

もちろん、ソケット通信のブロックが使用されている場合、メモリ枯渇の危険は潜んでいません。これはなぜでしょうか?すぐに Google にアクセスして、ブロッキング ソケットとは何かを調べてください。考えずに学ぶと無駄になり、学ばずに考えると危険にさらされます。

ネットリンクアドレス構造

TCP ブログ投稿では、インターネット プログラミング プロセスで使用されるアドレス構造と標準アドレス構造について言及しましたが、それらと Netlink アドレス構造との関係は次のとおりです:

struct sockaddr_nl{} の詳細な定義と説明は次のとおりです。

用户空间和内核空间通讯-Netlink 上

(ここ)をクリックして折りたたむか開く

  1. 構造体 sockaddr_nl
  2. {
  3. sa_family_t nl_family; /*このフィールドは常に AF_NETLINK です */
  4. unsigned short nl_pad; /* 現在は使用されていないため、0 で埋められます*/
  5. __u32 nl_pid; /* プロセス PID */
  6. __u32 nl_groups; /* マルチキャスト グループのマスク */
  7. };

nl_pid: この属性はメッセージの送受信のプロセス ID です。前に述べたように、Netlink はユーザーとカーネル空間の通信を実現するだけでなく、ユーザー空間の 2 つのプロセス間、または 2 つのプロセス間のリアルタイム通信も可能にします。カーネル空間内での通信。この属性が 0 の場合、通常は次の 2 つの状況に適用されます。

まず、送信したい宛先はカーネルです。つまり、ユーザー空間からカーネル空間に送信する場合、構築する Netlink アドレス構造の nl_pid は通常 0 に設定されます。ここで説明しなければならないことの 1 つは、Netlink 仕様では、PID の完全な名前は Port-ID (32 ビット) であり、その主な機能はネットリンク ベースのソケット チャネルを一意に識別することであるということです。通常、nl_pid は現在のプロセスのプロセス ID に設定されます。ただし、プロセスの複数のスレッドが同時にネットリンク ソケットを使用する場合、nl_pid の設定は通常次のように実装されます。

(ここ)をクリックして折りたたむか開く

pthread_self() << 16 | getpid();
  1. 2 番目に、マルチキャスト メッセージがカーネルからユーザー空間に送信されるとき、ユーザー空間プロセスがマルチキャスト グループ内にある場合、そのアドレス構造内の nl_pid も 0 に設定され、同時に、の別のプロパティを次の紹介文と組み合わせます。

nl_groups: ユーザー空間プロセスがマルチキャスト グループに参加したい場合は、bind() システム コールを実行する必要があります。このフィールドは、発信者が参加したいマルチキャスト グループ番号の

mask を指定します (グループ番号ではないことに注意してください。このフィールドについては後で詳しく説明します)。このフィールドが 0 の場合、発信者はどのマルチキャスト グループにも参加したくないことを意味します。 Netlink プロトコル ドメインに属する各プロトコルでは、最大 32 個のマルチキャスト グループをサポートできます (nl_groups の長さが 32 ビットであるため)。各マルチキャスト グループは 1 ビットで表されます。 Netlink の残りの知識ポイントについては、後の実践セッションで役立つ場合に説明します。

まだ終わっていないので、続きます...

以上がユーザー空間とカーネル空間間の通信に Netlink を使用するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlinuxprobe.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。