序文Linux カーネルプログラミングでは、マクロ関数 container_of(ptr, type, member) をよく見かけますが、ソースコードを辿っていくと、私たちのような一般人は絶望してしまいます (これは一体何なのか? 関数はこのように定義できるのか??? なぜ 0 があるのか??? ああ、忘れて、諦めよう...)。 カーネル マスターが強力になるのは、この部分です。たった 2 行のコードで、私たちは自分の人生を疑うようになります。すべてにはプロセスが必要なので、ゆっくりと進めてください。 実際、原理は非常に単純です。構造体型のメンバー member のアドレス ptr を指定して、構造体型の開始アドレスを見つけます。 type = ptr - size の開始アドレス (ここではバイト単位であるため、char * に変換する必要があります)。 ここまでで機能の説明は終わりです。簡単ですよね? ? ? 実はそうではありません。ここではサイズの計算方法については何も触れられておらず、それが私たちを混乱させています。 さて、関数プロトタイプのコンテナから始めましょう。 #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) 次は offserof 関数のプロトタイプです。 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) どうですか、かっこいいでしょう? さて、発表を始めましょう: 1. 0ポインタの使用(勝手に名付けたので、問題があるかは分かりません)事実がすべてを物語っています: #include <stdio.h> 構造体テスト { 文字 i ; 整数j; 文字k; }; int メイン() { 構造体テスト temp; printf("&temp = %p\n",&temp); printf("&temp.k = %p\n",&temp.k); printf("&((struct test *)0)->k = %d\n",((int)&((struct test *)0)->k)); } コンパイルして実行すると、次の結果が得られます。
私の言いたいことはおわかりでしょう。カスタム構造には i、j、k という 3 つの変数があります。 バイト アラインメント要件のため、構造体のサイズは 4 バイト * 3 = 12 バイトです。&((struct test *)0)->k の機能は、k から構造体 temp の開始アドレスまでのバイト数 (つまり、サイズ) を見つけることです。ここで 0 は struct test * 型に強制的に変換され、その役割は構造体の開始アドレスへのポインターとして機能することであり、& ((struct test *) 0) -> k の役割は k から開始ポインターまでのバイト数を計算することです。 。 。実際、相対アドレスを見つけるためです。開始アドレスは 0 なので、&k の値はサイズです (注: 印刷時に整数が必要なので、int への強制変換があります)。これにより、必要なサイズを見つけることができます。 さて、私は誤って offsetof() 関数の機能の説明を終えてしまいました: #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 今度は見た目が良くなりましたね?(なぜ最下層でこうなっているのかはまだわかりません…ただ、このように動作するということはわかります)、offsetof() の機能は、私たちが夢見ているサイズを見つけて、それを size_t の形式で返すことです(size_t: 符号なし整数)。 2. カーネルプログラミングの厳密さ#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) ここでは 2 行目だけを見ます。 const typeof( ((type *)0)->member ) *__mptr = (ptr); その機能は何ですか? 実際のところ、あまり効果はありません (批判しないでください、私の話を最後まで言わせてください) が、形式的には _mptr = ptr なので、なぜ同じ変数を定義する必要があるのでしょうか? ? ? 実はこれがカーネル開発者のすごいところです。使用時に開発者が入力したパラメータに問題がある場合、つまり ptr とメンバーの型が一致しない場合、コンパイル時に警告が出ますが、改行を削除すると警告は出なくなり、この警告はまさに必要です (エラーがどこにあるか分からないままエラーを防ぐため)。 。 。これは厳密ですよね? typeof( ((type *)0)->メンバー ) その機能はメンバーのタイプを取得することだけであり、それ以上のものではありません。これで基本的に終わりです 3. 結論container_of(ptr, type, member) 関数の実装は、次の 2 つの部分で構成されます。 1. ptrとメンバーが同じ型であるかどうかを判定する 2. サイズを計算します。構造体の開始アドレス = (type *)((char *)ptr - size) (注: 構造体ポインタへの強制変換) これで、container_of() の関数は、構造体変数のメンバーのアドレスを通じて構造体変数の最初のアドレスを見つけることだということがわかりました。 container_of(ptr,type,member)、ここで、ptr、type、member はそれぞれポインター、型、メンバーを表します。 Linux カーネル プログラミング コンテナの of() 関数に関するこの記事はこれで終わりです。Linux コンテナの of() 関数に関するその他の関連コンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM を応援していただければ幸いです。 以下もご興味があるかもしれません:
|
>>: html-cssタグのスタイル設定が機能しない2つの理由
簡単に言うと、distinct は重複を削除するために使用され、group by は統計を集計するよ...
目次1.mysqlダンプ実行プロセス:特徴2. CSVファイルをエクスポートする(最も柔軟性が高い)...
目次コンポーネントの分解左パネル右パネル入力パラメータの分解小道具スロット具体的な実装ドラッグする方...
外国のウェブサイトを開くと文字化けした文字が表示されることが多く、また、英語以外の外国のウェブサイト...
デフォルトでは、Docker はネットワーク化されていない UNIX ソケット上で実行されます。オプ...
Dockerfile は Docker イメージを構築するために使用されるファイルです。コマンドパラ...
目次地図フィルターいくつかの毎インデックスを検索パイプ参考回答1. パラメータを受け入れる関数を返す...
参考までにMySQL 8.0.22をダウンロードしてインストールしてください。具体的な内容は次のとお...
必ずデータをバックアップすることを忘れないでください。データは貴重なものです! ! !コンピュータモ...
序文Linux オペレーティング システムでは、デバイス ファイルは特別なタイプのファイルです。これ...
HTML テーブルを使用する場合、行間隔を変更する必要がある場合がありますが、余白、パディング、折り...
1. カスタムテキスト選択 ::選択{ 背景: 赤; 色: 黒; } 2. ビデオコントロールからダ...
ますます多くのウェブサイトで、XHTML が HTML4 に取って代わって急速に普及しています。しか...
今日、リモートデスクトップを実行してログインしているときにこのプロンプトを見つけました「MySQL ...
1.デバイス幅定義: 出力デバイスの画面表示幅を定義します。 Web ページが Safari で開か...