Linux カーネルプログラミングにおけるコンテナの of() 関数の紹介

Linux カーネルプログラミングにおけるコンテナの of() 関数の紹介

序文

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));
 
}

コンパイルして実行すると、次の結果が得られます。

&temp = 0xbf9815b4
&temp.k = 0xbf9815bc
&((構造体テスト*)0)->k = 8

私の言いたいことはおわかりでしょう。カスタム構造には 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 を応援していただければ幸いです。

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

<<:  最適なウェブページ幅とその互換性のある実装方法

>>:  html-cssタグのスタイル設定が機能しない2つの理由

推薦する

MySQL統計の概要

MySQL は、SQL 解析とクエリ最適化のプロセスを通じて SQL を実行します。パーサーは SQ...

iframeを指すaタグのターゲットの名前とIDの違い

コードをコピーコードは次のとおりです。 <iframe id="myFrameId&...

Nginx の構成と HTTP 実装コード分析との互換性

OpenSSL を使用して SSL キーと CSR ファイルを生成するHTTPS を設定するには、秘...

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

目次1. 構造体はメモリにどのように保存されますか? 2. container_ofマクロ3. 型4...

初心者がHTMLタグを学ぶ(1)

初心者は、いくつかの HTML タグを理解することで HTML を学習できます。この入門書は、初心者...

CSSアニメーションによるテーブルスクロールカルーセル効果の実装

前回の CSS 回転灯と同じ内容の CSS アニメーションの応用です。これは単なる別のアプリケーショ...

Dockerイメージのローカル移行の実装

最近 Docker を勉強しているのですが、よく問題に遭遇します。Docker イメージをダウンロー...

フロントエンドインタビューに必要なホモロジーとクロスドメインの詳細な説明

序文ご存知のとおり、ブラウザの相同性戦略とクロスドメイン方式も、フロントエンド面接で頻繁に遭遇する問...

MySQLデスクトップツールSQLyogのリソースとアクティベーション方法は、白黒のコマンドラインに別れを告げます

では、早速リソースについて見ていきましょう。 123WORDPRESS.COM ダウンロードSQLy...

CSS3 列を使用したカード ウォーターフォール レイアウトを実装するためのサンプル コード

この記事では、カード ウォーターフォール レイアウトを実現するための CSS3 列のサンプル コード...

Linux での vi (vim) の新しい使い方のまとめ

私は数年間 vi エディタを使ってきましたが、実用的な用途で使ったことはありませんでした。今日 Py...

シンプルなショッピングフォームを実装するJavaScript

この記事では、簡単なショッピングフォームを実装するためのJavaScriptの具体的なコードを参考ま...

nginx クッキーの有効期間に関する議論の要約

訪問するたびにブラウザにCookieが生成されますが、 Cookieの存在はユーザーにとって良いこと...

Navicatを使ってMySQLを操作する方法

目次序文: 1. Navicatの紹介2. シンプルなチュートリアルの共有接続管理ライブラリテーブル...

DockerはRedis5.0をビルドし、データをマウントします

目次1. 永続データの簡単なマウント2. DockerFileでイメージをビルドし、設定ファイルを指...