Linuxカーネルのcontainer_ofマクロを理解する


84

Linuxカーネルを閲覧しているときにcontainer_of、次のように定義されているマクロを見つけました。

container_ofの機能は理解できますが、理解できないのは最後の文です。

次のようにマクロを使用する場合:

最後の文の対応する部分は次のようになります。

何もしていないように見えます。誰かがここの隙間を埋めてくれませんか?


1
この回答に赤黒木を使用した実際の直感的な例がありますrb_node
qeatzy 2017

回答:


87

container_of(dev, struct wifi_device, dev);そこに2つの名前空間を混在させているため、使用例は少し誤解を招く可能性があります。

dev例の最初の例はポインターの名前を参照していますが、2番目の例はdev構造体メンバーの名前を参照しています。

おそらく、この混乱がすべての頭痛の種を引き起こしています。実際、member見積もりのパラメーターは、コンテナー構造内のそのメンバーに付けられた名前を参照しています。

このコンテナを例にとると:

また、マクロを使用してポインタを取得するメンバーint *my_ptrへのポインタは、this_data次を使用して取得しますstruct container *my_container

this_data構造体の先頭へのオフセットを考慮に入れることは、正しいポインター位置を取得するために不可欠です。

事実上、正しい場所を取得するにはthis_data、ポインターmy_ptrからメンバーのオフセットを引くだけです。

それはまさにマクロの最後の行が行うことです。


5
より詳細な説明が必要な方のために:Radek Pazdera 彼のブログでcontainer_ofマクロ(include/linux/kernel.h)を非常に明確に説明ましたところでlist_entryマクロ(include/linux/list.h)は以前は非常によく似て定義されていましたが、現在はとして定義されていcontainer_ofます。
patryk.beza 2016

1
また、Greg Kroah-Hartmanによるこのブログ投稿が役立つ場合があります:kroah.com/log/linux/container_of.html
EFraim

1
原則として、それは質問のかなりの部分に実際には答えないことを除いて、良い答えですか?たとえば、コードの最後の行は何をどのように達成しますか?
マイケルビアー

この例に従うと、GCCを使用するとerror: initialization from incompatible pointer type。これconstは、このスレッドによるマクロ定義のせいですか?stackoverflow.com/a/39963179/1256234
アンディ・J

18

最後の文のキャスト:

指定されたへのポインタtype。ポインターは、指定されたポインターからのオフセットとして計算されますdev

cointainer_ofマクロを使用するときは、特定のフィールドのポインターを含む構造を取得する必要があります。例えば:

構造体の中央を指すポインターがありますが(これは、フィールドtwo[構造体のフィールド名]へのポインターであることがわかっています)、構造体全体を取得したいと考えています(numbers)。したがって、two構造内のファイルのオフセットを計算します。

そして、与えられたポインタからこのオフセットを引きます。結果は、構造体の開始へのポインターです。最後に、このポインターを構造体タイプにキャストして、有効な変数を設定します。


10

これは、gcc拡張、ステートメント式の利用です。マクロが値を返すものとして表示される場合、最後の行は次のようになります。

複合ステートメントの説明については、リンク先のページを参照してください。ここに例があります:

出力は

b 25


7

Linuxカーネルのconatainer_of()マクロ-

コード内の複数のデータ構造を管理する場合、ほとんどの場合、メモリオフセットや境界について質問されることなく、ある構造を別の構造に埋め込み、いつでも取得する必要があります。ここで定義されているように、構造体の人がいるとしましょう。

年齢または給与に関するポインターを設定するだけで、そのポインターをラップ(含む)構造全体を取得できます。名前が示すように、container_ofマクロは、構造体の特定のフィールドのコンテナーを見つけるために使用されます。マクロはinclude / linux / kernel.hで定義されており、次のようになります。

ポインタを恐れないでください。次のようにそれらを見てください:

上記のコードフラグメントの要素は次のとおりです。

  • ポインタ:これは構造体のフィールドへのポインタです
  • container_type:これはポインターをラップする(含む)構造のタイプです
  • container_field:これは、構造内でポインターが指すフィールドの名前です。

次のコンテナについて考えてみましょう。

それでは、そのインスタンスの1つと、年齢メンバーへのポインターについて考えてみましょう。

名前メンバー(age_ptr)へのポインターとともに、container_ofマクロを使用して、以下を使用して、このメンバーをラップする構造全体(コンテナー)へのポインターを取得できます。

container_ofは、構造体の先頭の年齢のオフセットを考慮して、正しいポインター位置を取得します。ポインタage_ptrからフィールドageのオフセットを引くと、正しい場所が得られます。これは、マクロの最後の行が行うことです。

これを実際の例に適用すると、次のようになります。

container_ofマクロは、主にカーネルの汎用コンテナーで使用されます。

カーネルのcontainer_ofマクロについては以上です。


3

少し現実的な文脈はより明確に言っています、以下は例として赤黒木を使用します、それは私が理解する方法ですcontainer_of

通りDocumentation/rbtree.txtの状態、Linuxカーネルのコードでは、それはrb_nodeむしろ、データエントリが含まれていません

rbtreeツリーのデータノードは、structrb_nodeメンバーを含む構造体です。

struct vm_area_struct(ファイル内include/linux/mm_types.h:284)はそのような構造です、

同じファイルに、rb_entry次のように定義されているマクロがあります。

明らかに、rb_entryと同じcontainer_ofです。

で、mm/mmap.c:299内部関数定義browse_rbの使用方法がありますrb_entry

今では明らかです、でcontainer_of(ptr, type, member)

  • type コンテナ構造体です、ここに struct vm_area_struct
  • membertypeインスタンスのメンバーの名前です。ここvm_rbでは、タイプはrb_node
  • ptrここmemberでは、typeインスタンスを指すポインタですrb_node *nd

container_ofこの例のように、何をしますか

  • アドレス指定されたobj.member(ここではobj.vm_rb)、のアドレスを返しますobj
  • 構造体は連続したメモリのブロックであるため、obj.vm_rbマイナス offset between the struct and memberのアドレスがコンテナのアドレスになります。

include/linux/kernel.h:858 -の定義 container_of

include/linux/rbtree.h:51 -の定義 rb_entry

mm/mmap.c:299 -の使用法 rb_entry

include/linux/mm_types.h:284 - struct vm_area_struct

Documentation/rbtree.txt: -赤黒木のドキュメント

include/linux/rbtree.h:36 -の定義 struct rb_node

PS

上記のファイルは現在の開発バージョン、つまりです4.13.0-rc7

file:kの平均k行目file



0

Container _ofマクロの最も単純な実装は以下のとおりです。これにより、タイプの複雑なチェックがすべて削減され、機能します

ptrはメンバーのアドレスを提供し、オフセットの差を差し引くだけで、開始アドレスを取得します。

使用例

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.