LinuxカーネルマクロContainer_Ofの詳細な説明

LinuxカーネルマクロContainer_Ofの詳細な説明

1. 構造体はメモリにどのように保存されますか?

int メイン() 
{ 
 
 学生スタ; 
 スタッ.id = 123456; 
 strcpy(stu.name,"feizhufeifei"); 
 stu.math = 90; 
 ステュー.PE = 80; 
 printf("生徒:%p\r\n",&stu); 
 printf("stu.ID:%p\r\n",&stu.ID); 
 printf("stu.name:%p\r\n",&stu.name); 
 printf("stu.math:%p\r\n",&stu.math); 
 0を返します。 
} 

印刷結果は次のとおりです。

//構造体のアドレス Student:0xffffcbb0 
//構造体の最初のメンバーのアドレス stu.ID:0xffffcbb0 //オフセットアドレス + 0 
stu.name:0xffffcbb4//オフセットアドレス + 4 
stu.math:0xffffcbd4 //オフセットアドレス + 24 


構造体のアドレスは構造体の最初のメンバーのアドレスと同じであることがわかります。これが、「車輪の再発明を拒否! Linux カーネルの一般的なリンク リストを移植して使用する方法 (完全なコード実装付き)」で、構造体の最初に struct list_head を配置する必要がある理由について説明した理由です。

よくわからない場合は、次の 2 つの例を見てください。

  • struct A { int a; char b; int c; char d; }; a はオフセット 0、b はオフセット 4、c はオフセット 8 (4 + 1 より大きい 4 の最小の整数倍)、d はオフセット 12 にあります。 A は配置 4 とサイズ 16 です。
  • struct B { int a; char b; char c; long d; }; a はオフセット 0、b はオフセット 4、c はオフセット 5、d はオフセット 8 にあります。 B の配置は 8、サイズは 16 です。

構造体のメンバー変数は実際にはメモリ内のオフセット アドレスとして格納されていることがわかります。つまり、構造体 A のアドレス + メンバー変数のオフセット アドレス = 構造体メンバー変数の開始アドレスです。

したがって、構造体変数の開始アドレスとメンバー変数のオフセット アドレスに基づいて、構造体 A のアドレスを推測することもできます。

2. container_ofマクロ

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*​​)0)->MEMBER) 
#define container_of(ptr, type, member) ({ \ 
        const typeof(((type *)0)->member)*__mptr = (ptr); \ 
    (type *)((char *)__mptr - offsetof(type, member)); }) 


まず、3 つのパラメータを見てみましょう。ptr はメンバー変数へのポインタ、type は構造体の型、member はメンバー変数の名前です。

container_of マクロの機能は、構造体内のメンバー変数のアドレス、変数名、および構造体の型を通じて構造体変数のアドレスを見つけることです。ここで使用されるトリックは、コンパイラ テクノロジを使用することです。これは、まず構造体内の構造体メンバーのオフセットを見つけ、次にメンバー変数のアドレスに基づいてメイン構造体変数のアドレスを見つけるというものです。以下、各部分の詳細な分析です。

3. 型

まず、変数の型を返すために使用される typeof を見てみましょう。これは GCC コンパイラの拡張機能であり、typeof はコンパイラに依存します。これは C 言語仕様では必須ではなく、標準の一部でもありません。

int メイン() 
{ 
 整数a = 5; 
 //ここでaと同じ型の変数bを定義します 
 型aはb = 6です。 
 printf("%d,%d\r\n",a,b);//5 6 
 0を返します。 
} 

4. (((タイプ *)0)->メンバー)

((TYPE *)0) は、0 を type 型の構造体ポインタに変換します。言い換えると、コンパイラは構造体がプログラム セグメントの先頭 0 から始まると考えます。アドレス 0 から始まる場合、取得するメンバー変数のアドレスは、メンバー変数のオフセット アドレスと直接等しくなります。

(((type *)0)->member) は、構造体内の MEMBER メンバーを参照します。

typedef 構造体学生{ 
 整数ID; 
 文字名[30]; 
 整数数学; 
}学生; 
int メイン() 
{ 
 //ここでは、構造体が強制的にアドレス 0 に変換され、その後、name のアドレスが出力されます。 
 printf("%d\r\n",&((生徒 *)0)->名前);//4 
 0を返します。 
} 

5. const typeof(((type * )0) ->member)*__mptr = (ptr);

このコードは、 typeof()を使用して構造体のmember属性の型を取得し、この型の一時ポインター変数__mptrを定義し、 ptrが指すmemberのアドレスを__mptrに割り当てることを意味します。

なぜこれを行う代わりにptrを使用しないのでしょうか? ptrprtによって指される内容が損傷するのを避けるためだと思います。

6. offsetof(型、メンバー)

((size_t) &((TYPE*​​)0)->メンバー)

size_t は標準 C ライブラリで定義されており、通常 32 ビット アーキテクチャでは次のように定義されます。

typedef 符号なし int size_t;

64 ビット アーキテクチャでは次のように定義されます。

typedef 符号なし long size_t;

定義からわかるように、size_t は負でない数なので、通常はカウントに size_t が使用されます (カウントには負の領域は必要ないため)。

(size_t i=0;i<300;i++) の場合

プログラムの移植性を高めるために、カーネルは int と unsigned の代わりにsize_tを使用しますunsigned。((size_t) &((TYPE*)0)->MEMBER)前の説明と組み合わせると、この文は0アドレスを基準としたMEMBERのオフセット値を見つけることを意味していることがわかります。

7. (type * )((char * )__mptr - offsetof(type, member))

この文は、 __mptr char* 類型に変換することを意味します。 offsetofによって取得されるオフセットはバイト単位であるためです。 2 つを減算して構造体の開始位置を取得し、それを強制的にtypeに変換します。

8. 例

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 
#define container_of(ptr, type, member) ({ \ 
        const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 
        (type *)( (char *)__mptr - offsetof(type,member) );}) 
         
typedef 構造体 学生 
{ 
 整数ID; 
 文字名[30]; 
 整数数学; 
}学生; 
 
int メイン() 
{ 
    学生スタ; 
        学生 *sptr = NULL; 
  スタッ.id = 123456; 
  strcpy(stu.name,"zhongyi"); 
  stu.math = 90; 
        sptr = container_of(&stu.id,学生、id); 
        printf("sptr=%p\n",sptr); 
        sptr = container_of(&stu.name,生徒,名前); 
        printf("sptr=%p\n",sptr); 
        sptr = container_of(&stu.math,学生,id); 
        printf("sptr=%p\n",sptr); 
        0を返します。  
} 


結果は次のとおりです。

0xffffcb90 のプロパティ
0xffffcb90 のプロパティ
0xffffcbb4 の続きを読む

マクロ展開でより明確になるかもしれない

int メイン() 
{ 
    学生スタ; 
        学生 *sptr = NULL; 
  スタッ.id = 123456; 
  strcpy(stu.name,"zhongyi"); 
  stu.math = 90; 
  //展開して置換 sptr = ({ const unsigned char *__mptr = (&stu.id); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->id) );}); 
        printf("sptr=%p\n",sptr); 
        //展開して置換 sptr = ({ const unsigned char *__mptr = (&stu.name); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->name) );}); 
        printf("sptr=%p\n",sptr); 
        //展開して置換 sptr = ({ const unsigned int *__mptr = (&stu.math); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->math) );}); 
        printf("sptr=%p\n",sptr); 
        0を返します。  
} 

LinuxカーネルのContainer_Ofマクロの詳細説明はこれで終了です。LinuxカーネルのContainer_Ofマクロの詳細については、123WORDPRESS.COMの過去の記事を検索するか、以下の関連記事を引き続き閲覧してください。今後とも123WORDPRESS.COMをよろしくお願いいたします。

以下もご興味があるかもしれません:
  • C言語コンテナof()関数のケース詳細説明
  • Linux カーネルプログラミングにおけるコンテナの of() 関数の紹介
  • Linuxカーネルマクロcontainer_ofの詳細な分析
  • Linuxカーネルのcontainer_of関数の詳細な説明
  • C言語マクロ関数コンテナof()の紹介

<<:  実用的な MySQL + PostgreSQL バッチ挿入更新 insertOrUpdate

>>:  JSコンストラクタとインスタンス化およびプロトタイプ導入の関係

推薦する

MySQL 5.x 以降を使用している場合のエラー #1929 列 ''createtime'' の日付時刻値が正しくありません: '''' の簡単な解決方法

MySQL をインストールした後、テーブル データを保存および削除しようとすると、常にエラー メッセ...

Tomcatにデプロイされたアプリケーションがフロントエンドページにアクセスできない問題について

最近、実践的なトレーニング プロジェクトを実行する際に ssm フレームワークを使用しました。プロジ...

CSS3 変換によって子要素の固定位置を絶対位置に変更する方法

この記事では、CSS3 の transform を使用して子要素の固定配置を絶対配置に変更する方法を...

IE アドレスバーのアイコン表示問題を解決する 3 つの手順

<br />この Web ページ制作スキル チュートリアルは、Web サイトのアイコンを...

プレーンな JS オブジェクトの代わりに Map を使用する場合

目次1. マップは任意のタイプのキーを受け入れます2. マップにはキー名に関する制限はありません3....

モバイルウェブページのサイズ調整を実装する方法

ようやく手元のプロジェクトが終了し、行方不明だった人たちが戻ってきました!プロジェクトを進める過程で...

docker compose を使用して FastDfs ファイル サーバーをインストールする詳細な例

ドッカーの作成 バージョン: '2' サービス: fastdfsトラッカー: ホスト...

MySQL データベースの鉄則 (要約)

適切なデータベース仕様は、ソフトウェア実装の複雑さを軽減し、通信コストを削減するのに役立ちます。この...

CSSマウスを画像の上に置いたときにマスクレイヤー効果を追加する実装

まず効果を見てみましょう: マウスを画像の上に移動すると、影の効果とテキスト/アイコンが追加されます...

MySQL データ型 DECIMAL(N,M) における N と M の意味の詳細な説明

同僚から、MySQL データ型 DECIMAL(N,M) の N と M の意味を尋ねられました。言...

純粋な CSS ヘッダーの実装コードを修正

純粋な CSS で固定ヘッダーを実装するのが難しい主な理由は 2 つあります。まず、最大のシェアを持...

HTMLテキストオーバーフローの2つの一般的な解決策は省略記号を表示することです

方法1: CSSオーバーフロー省略を使用して解決する解決策は次のとおりです。 CSSコード: ディス...

nginx で http でアクセスする Web サイトを https に変更する方法

目次1. 背景2. 前提条件https:証明書システム: 3. 操作プロセス3.1 証明書の生成3....

Angular構造ディレクティブモジュールとスタイルの詳細な説明

目次1. 構造指示モジュールforRoot()を書く3. スタイルの定義ドラッグ時の順序を調整するに...

CnBlogs カスタムブログスタイルの共有

半夜かけてようやくブログのスタイルを大体完成させることができました。ブログ全体が青を基調としていて、...