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コンストラクタとインスタンス化およびプロトタイプ導入の関係

推薦する

弾力性のあるナビゲーション効果を実現するJavaScript

この記事では、弾性ナビゲーション効果を実現するためのJavaScriptの具体的なコードを参考までに...

Dockerはプライベートライブラリイメージを完全に削除します

まず、インターネット上の一般的な慣行を見てみましょうデフォルトでは、プライベート ライブラリはイメー...

MYSQLパターンマッチングREGEXPの使用に関する一般的な話など

のようにLIKE ではデータ全体が一致する必要がありますが、REGEXP では部分的な一致のみが必要...

HTMLは読み取り専用のテキストボックスを実装しており、コンテンツを変更することはできません。

さっそく、コードを直接投稿します。具体的なコードは次のとおりです。 <!--方法 1: onf...

WMLとは何ですか?

WML (ワイヤレス マークアップ言語)。これは HTML から派生したマークアップ言語ですが、W...

VueコンポーネントライブラリElementUIはテーブルリストのページング効果を実現します

ElementUIはテーブルリストのページング効果のチュートリアルを実装しています。参考までに。具体...

Linux システムに Spring Boot アプリケーションをインストールするための詳細なチュートリアル

Unix/Linux サービスsystemd サービス操作プロセス1. JDKがインストールされたC...

Chrome、Firefox、IEで入力カーソルの位置がずれる問題の解決方法

ブラウザで入力カーソルがずれる問題の詳しい説明<br />仕事で問題に遭遇し、解決策を探...

MySQL 8.0.16 圧縮パッケージのインストールと設定方法のグラフィックチュートリアル

この記事では、MySQL 8.0.16圧縮パッケージのインストールと設定方法を参考までに紹介します。...

読み取り専用と無効の違い

要約すると: Readonly は入力 (テキスト/パスワード) とテキスト領域に対してのみ有効です...

MySQLテーブル名の大文字と小文字を区別しない設定方法の詳細な説明

デフォルトでは、Linux の MySQL はテーブル名の大文字と小文字を区別します。 MySQL ...

CSS3はNESゲームコンソールのサンプルコードを実装します

成果を達成する実装コードhtml <input type="radio" ...

MySQL ルートパスワードエラー番号 1045 の解決方法

MySQLサービスを停止するWindowsでは、マイコンピュータを右クリック--管理--サービスと...

Nginx の add_header ディレクティブに注意する必要があるのはなぜですか?

序文ご存知のとおり、nginx 構成ファイルは add_header ディレクティブを使用して応答ヘ...

mysql 8.0.16 winx64.zip インストールと設定方法のグラフィックチュートリアル

この記事では、MySQL 8.0.16 winx64.zipのインストールと設定方法の具体的なコード...