最初のマクロカーネル

最初のマクロカーネル

LIST_ENTRY()は構造部材によってポインタ構造体へのポインタを返すように設計された第一のコアマクロ名を有しています。私たちは、最初のマクロデザインのカーネルの機微を感じ、それの謎を解明するために分析してステップバイステップでみましょう。

仕上げ分析の考え方

次のようにLIST_ENTRY()は、カーネルlist.hソース/含む/ Linuxディレクトリに定義されています。

#define list_entry(ptr, type, member) \
                container_of(ptr, type, member)

定義リスト_entryでは、我々は他のマクロコンテナ_ofの出現を参照してください。そして、それは達成するために、このマクロリスト_entryコンテナ_ofています。したがって、我々はまず、それが何をするかを見て、コンテナ_ofを入力する必要があります。

/include/linux/kernel.hで定義されてcontainer_of、次のように定義されました:

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

私たちは、container_ofの定義にそれを見つけましたが、また新しいマクロoffsetofはの登場。あなたは、コンテナ_ofの分析を開始する前に、offsetofはクリアする必要があります。

次のように定義され、/include/linux/stddef.hで定義されoffsetofは:

#define offsetof(TYPE, MEMBER)  ((size_t) &((TYPE *)0)->MEMBER)

私たちは、分析のための突破口としてoffsetofはに持っているので、定義の中で、それはもはや、新しいマクロを導入された、offsetofはことを見ることはできません。

フォーマル解析

マクロoffsetofは

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

私たちは名前、可能offsetofはマクロの役割を示唆し、関連するオフセットを見ることができるようにWordが、手段のオフセットを相殺しました。それを相殺必要であるだけでなく、?

  • オフセットMEMBERメンバーを計算するための型構造offsetofは。

offsetofははAT&((TYPE *)0)の定義から見ることができます - > MEMBERは、明確なキャスト((TYPE *)0)があります。C言語では、キャストが2構文は次のとおりです。

1.(TYPE)var_name; //变量名形式,如(int)i;
2.(TYPE)varlue;   //值形式,如(type*)0;

2番目の構文、ポインタ型構造に値0キャストの定義に使用されます。この後のキャストアドレス型構造により、なぜこのような変換を行い、0となり?その役割は何ですか?
実際には、それが簡単にオフセットのメンバーを取得するようにすることですので、一つだけの目的を、やっています。我々は、迅速アクセスデータ及び保存ストレージスペースにCPUを可能にするために、プリコンパイル時に構造の種類を知っている、メモリのアライメント問題が構造の各メンバは、特定のに応じて、メモリに格納することです店舗オフセット。我々は一定のオフセット値を設定するすべてのメンバーに一度、すべてのためにすることはできませんそのため、各部材のオフセットを生じる部材の異なる種類に起因は、同じではありません。その後、我々はそれを行う方法のメンバーのオフセットを取得したいですか?私たちは、コンパイラにこのタスクを置きます。私たちは、オフセットメンバー「ハンドオーバー」を、それをコンパイラに指示することができます。私たちは各メンバーのオフセットを、事前にコンパイラのコンパイル時に、明確でなければならないことの一つは、そのをよく知っているので、あなたがメンバーのアドレスを知りたい場合、コンパイラは、それだけでアドレスの構造が必要+オフセットメンバーは、メンバーのアドレスを取得することができます。
ここに画像を挿入説明

簡単な例として上記のチャートでは、例えば、C = 1000上のPのアドレス構造場合,,メンバーが(オフセット)オフセット4であり、部材のアドレスがC = 1004 1000年PC + 4です。

1004のアドレスがC PCのメンバーであるが、私たちが望む得るために、この時間は、そのアドレスではないが、それは、それを行うにはどのようにこの時間を相殺していますか?最も簡単な方法は、直接構造がそれを行うことはできませんゼロになる対処するのですか?0プラス数は数自体に等しく、これは、オフセット部材を追加するだけの結果です。鋳造組織のアドレスで定義するには、例えば、0になった理由は以下のとおりです。

今、アドレスp = 0の構造は、C部材は(オフセット)オフセット+ 4 = 4,0または4、結果が正確部材からオフセットされています。

> MEMBERこの文、それはそのようなことを行うことです、それは最初のアドレスタイプ型の構造が0になり、その後、メンバーを追加するために行く - コンパイラが行う&((TYPE *)0)してみましょう、私たちはそう部材はオフセット0 +オフセット=オフセット、最終結果は、メンバーのオフセットされています。カーネルの設計者は、それがオフセット生成するコンパイラを指示するために、この巧妙な設計によるです。

だから、我々は(TYPE、MEMBER)offsetofは呼び出したときに、部材の構造にオフセットメンバー型を与えます

あるについてここでは、価値の考え方がある:&((TYPE *)0) - > MEMBER、義務的変換により0に構造体のアドレス、
我々はアドレス0が使用するオペレーティングシステム用に予約されて知っている、とありますコンテンツは、一般的な手順にアクセスすることを許可されていません。しかし、ここで構造アドレス0、0に入れ、直接アドレスは、プログラムがクラッシュすることはありませんこと?

答えは、プログラムがクラッシュしないということであるコンパイラの実行&((TYPE *)0) - > MEMBER 時間の、0の内容にアクセスしないように、実際にアドレスを行いますが、唯一の操作で値0に加えとして加数に対処します。その画像は、コンパイラがただ、部屋の計算を行うために使用されるあなたの家の番号を脱いでドアを開けて家の中で何かを拾うていなかったです。添加後の仕上げにそれを残し、家は無傷である何かです。「&」存在するため、コンパイラは、家に物事を取らないという理由は、コンパイラが見ている「&」、あなたは、私はちょうどそれにアドレスを取得する必要があることを理解するであろう。示すために、ここで簡単な例:

ここに画像を挿入説明

次のように印刷結果は以下のとおりです。

ここに画像を挿入説明

根据打印结果可以看到:pst->j与&(pst->j)效果是不一样的
pst->j		//没有“&”,会访问变量中的内容,打印结果为成员变量中的内容
&(pst->j)	//有“&”,不会访问变量中的内容,只拿地址,打印结果为成员的地址

この時点で、我々offsetofはの役割は既に知っています。中_ofコンテナの実現に、と言うことですoffsetofは使用container_ofの定義では、それはoffsetofは、構造体のメンバのオフセットを取得する必要があります、その後、コンテナ_ofの役割は何ですか?それはそれが何であるかを使用相殺するのですか?次に、私たちはそれ_of世界のコンテナを入力してみましょう。

マクロcontainer_of

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

世界_ofコンテナを入力した後、我々は二つの「おなじみの見知らぬ人」があることがわかりtypeof演算であり、「({})。」我々は、彼らが見ていなかったC言語にある2人の小さなパートナーは、それだけでGNU Cコンパイラでは、彼らの「ライブ」ためです。旅_of理解コンテナで私たちのためにそれを容易にするために、彼らが何であるかを学ぶために、友達を作るためにこれら二つの卓越したジュニアパートナー、我々はいくつかの時間を費やす必要があるとtypeof演算と「({})」。

typeof演算
  • typeof演算は、GNU Cコンパイラ固有のキーワードです
  • typeofをコンパイル時にのみ有効になり、変数の型に慣れます

例えば:

int i = 100;
typeof(i) j = i; <=> int j = i;  //这两个语句的作用是等价的,变量i的类型是int,typeof(i)就相当于拿到变量i的类型
({})
  • ({})構文拡張は、GNU Cコンパイラであります
  • ({})同様コンマ式、結果は、最後の文の値であります

例えば:

ここに画像を挿入説明

さて、私たちはこのような理解_ofコンテナは、私たちに大きな助けになるだろう、typeof演算と「({})」二つの小さなパートナーを認識しています。今、私たちは、マクロ_of分析容器正式にすることができます。ここに移動_ofコンテナの定義に、再び私たちをみましょう:

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

拡張構文の定義に使用される「({})」まだ、私たちは直接最後の文を見ることができ、それは価値の最後の文の結果である、と言われてきました。

(タイプ*)((CHAR *)__ mptr - offsetofは(タイプ、メンバー))。

2行目で定義されているポインタ__mptr、typeof演算により得られたタイプがあります。また、構造体用部材部材を入力__mptr点ポインタptrの__mptrのポインタ値は同じであり、ポインタ型構造体のメンバ部材であるマクロ_ofパラメータPTR容器です。明らかにこの関係を表現するために、我々は次の図の彼らの関係を表現するための図を使用します。

我々は見て(文字)__ mptr - offsetofは、この文の意味は何か(タイプ、メンバーの)。(タイプ、部材)部材部材をオフセットさoffsetofは、上記画像を上記のようにアドレス、Pを得るために、次にオフセット__mptrを減算する、オフセット、およびアドレスはアドレス構造であることにより得られます、したがってメンバーによって発見構造体の先頭アドレスを達成します。__mptrチャー以上が効果バイト単位の減算にポインタ演算のためです。最後に(タイプ*)は、構造体へのポインタとしてキャスト。

ここでは、謎container_ofマクロは終わりました。

**考慮ポイントの価値があります:** __mptr =(PTR)以来、ちょうどPTRを減らすために渡されたパラメータが、一見「余分」セカンドライン割り当てPTRの値を使用しない理由__mptr、それをカットする__mptr?

答えはセキュリティチェックの種類の入ってくるパラメータへ。マクロは、コンパイル時にプリプロセッサによって処理されます。シンプルなテキスト置換を行うためのプリプロセッサは、不注意のエラーによるコードの作成、中に私たちを導く可能性がある、任意の型チェックは行いません。例えば、(PTR、タイプ、_ofコンテナ部材)は3つのパラメータを持って、入ってくるPTR場合、我々は、不注意に、間違ったptrがポインタは、渡されたプログラムが正しく実行されていることを見つけましたが、結果は間違っています。型安全性チェックの少しを持っていることができるようにするために、コードのセキュリティを高めるために、この時間ので、2行目のセキュリティチェックのタイプを追加するためにコードの行を定義する場合、_of容器の定義におけるカーネルデザイナーあなたはポインタ、ポップアップ警告を渡すとき、それは間違っているだろう、この警告は、タイプがこの場所に互換性がない場合は、我々はこれBUGを避け、それが再びパラメータをチェックし実行する前に行くことができるように、あることを教えてくれる。

エピローグ

これまでのところ、我々は明らかにcontainer_ofの役割を知っています。今、私たちは、元の出発点---リスト_entry()にあり、それは最初のマクロのカーネルと呼ばれている理由を理解します。

公開された40元の記事 ウォン称賛18 ビュー7582

おすすめ

転載: blog.csdn.net/weixin_44395686/article/details/99365705