Linuxカーネルをブラウズしていたときに、container_of
マクロは次のように定義されています。
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
Container_ofの機能は理解していますが、理解できないのは最後の文です。
(type *)( (char *)__mptr - offsetof(type,member) );})
次のようにマクロを使用する場合:
container_of(dev, struct wifi_device, dev);
最後の文の対応する部分は次のとおりです。
(struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev);
何もしないように見えます。誰でもここの空所を埋めていただけますか?
使用例container_of(dev, struct wifi_device, dev);
は、2つの名前空間が混在しているため、少し誤解を招くかもしれません。
例の最初のdev
はポインターの名前を指しますが、2番目のdev
は構造体メンバーの名前を指します。
ほとんどの場合、この混同は頭痛の種のすべてを引き起こしています。実際、引用符のmember
パラメーターは、コンテナー構造内のそのメンバーに与えられた名前を参照します。
このコンテナを例にとると:
struct container {
int some_other_data;
int this_data;
}
また、マクロを使用してint *my_ptr
メンバーへのポインターthis_data
を使用して、struct container *my_container
へのポインターを取得します。
struct container *my_container;
my_container = container_of(my_ptr, struct container, this_data);
this_data
のオフセットを構造体の先頭まで考慮することは、正しいポインター位置を取得するために不可欠です。
事実上、正しい位置を取得するには、ポインターthis_data
からメンバーmy_ptr
のオフセットを差し引くだけです。
それがまさにマクロの最後の行が行うことです。
最後の文キャスト:
(type *)(...)
指定されたtype
へのポインター。ポインターは、特定のポインターdev
からのオフセットとして計算されます。
( (char *)__mptr - offsetof(type,member) )
cointainer_of
マクロを使用する場合、特定のフィールドのポインターを含む構造を取得する必要があります。例えば:
struct numbers {
int one;
int two;
int three;
} n;
int *ptr = &n.two;
struct numbers *n_ptr;
n_ptr = container_of(ptr, struct numbers, two);
構造体の中央を指すポインターがあります(それがフィールドtwo
[構造体内のフィールド名]へのポインターであることはわかっていますが、構造全体(numbers
)を取得します。したがって、構造体のフィールドtwo
のオフセットを計算します。
offsetof(type,member)
指定されたポインタからこのオフセットを減算します。結果は、構造の開始点へのポインターです。最後に、このポインターを構造型にキャストして、有効な変数を取得します。
これは、gcc拡張機能 statements expression の利用です。マクロが値を返すものとして表示される場合、最後の行は次のようになります。
return (struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev);
複合ステートメントの説明については、リンク先のページをご覧ください。以下に例を示します。
int main(int argc, char**argv)
{
int b;
b = 5;
b = ({int a;
a = b*b;
a;});
printf("b %d\n", b);
}
出力は
b 25
例として赤黒木を使用、これは私が_container_of
_を理解する方法です。
_Documentation/rbtree.txt
_が示すように、Linuxカーネルコードでは、rb_nodeにはデータエントリが含まれず、むしろ
Rbtreeツリーのデータノードは、struct rb_nodeメンバーを含む構造です。
_struct vm_area_struct
_(ファイル_include/linux/mm_types.h:284
_内)はそのような構造です。
同じファイル内に、マクロ_rb_entry
_があり、次のように定義されています
_#define rb_entry(ptr, type, member) container_of(ptr, type, member)
_
明らかに、_rb_entry
_は_container_of
_と同じです。
関数定義_mm/mmap.c:299
_内の_browse_rb
_には、_rb_entry
_の使用法があります。
_static int browse_rb(struct mm_struct *mm)
{
/* two line code not matter */
struct rb_node *nd, *pn = NULL; /*nd, first arg, i.e. ptr. */
unsigned long prev = 0, pend = 0;
for (nd = rb_first(root); nd; nd = rb_next(nd)) {
struct vm_area_struct *vma;
vma = rb_entry(nd, struct vm_area_struct, vm_rb);
/* -- usage of rb_entry (equivalent to container_of) */
/* more code not matter here */
_
container_of(ptr, type, member)
で明確になりました。
type
はコンテナ構造体です。ここでは_struct vm_area_struct
_member
はtype
インスタンスのメンバーの名前です。ここでは_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
_の定義
P.S。
上記のファイルは現在の開発バージョン、つまり_4.13.0-rc7
_にあります。
_file:k
_は、file
のk行目を意味します。
linuxカーネルのconatainer_of()マクロ-
コードで複数のデータ構造を管理する場合、ほとんどの場合、1つの構造を別の構造に埋め込み、メモリオフセットまたは境界について質問されることなく、いつでもそれらを取得する必要があります。ここで定義されているように、struct personがあるとしましょう:
struct person {
int age;
int salary;
char *name;
} p;
年齢または給与にポインタを置くだけで、そのポインタを包む(含む)構造全体を取得できます。名前が示すように、container_ofマクロは、構造の指定されたフィールドのコンテナーを見つけるために使用されます。マクロはinclude/linux/kernel.hで定義され、次のようになります。
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
ポインターを恐れないでください。次のように表示するだけです。
container_of(pointer, container_type, container_field);
上記のコードフラグメントの要素は次のとおりです。
次のコンテナを考えてみましょう。
struct person {
int age;
int salary;
char *name;
};
それでは、年齢メンバーへのポインタとともに、そのインスタンスの1つを考えてみましょう。
struct person somebody;
[...]
int *age_ptr = &somebody.age;
名前メンバー(age_ptr)へのポインターとともに、次を使用してこのメンバーをラップする構造全体(コンテナー)へのポインターを取得するためにcontainer_ofマクロを使用できます。
struct person *the_person;
the_person = container_of(age_ptr, struct person, age);
container_ofは、構造体の最初の年齢のオフセットを考慮して、正しいポインターの位置を取得します。ポインターage_ptrからフィールドageのオフセットを引くと、正しい場所が得られます。これは、マクロの最後の行が行うことです:
(type *)( (char *)__mptr - offsetof(type,member) );
これを実際の例に適用すると、次のようになります。
struct family {
struct person *father;
struct person *mother;
int number_of_sons;
int family_id;
} f;
/*
* Fill and initialise f somewhere */ [...]
/*
* pointer to a field of the structure
* (could be any (non-pointer) member in the structure)
*/
int *fam_id_ptr = &f.family_id;
struct family *fam_ptr;
/* now let us retrieve back its family */
fam_ptr = container_of(fam_id_ptr, struct family, family_id);
Container_ofマクロは、主にカーネルの汎用コンテナーで使用されます。
これがカーネルのcontainer_ofマクロのすべてです。
Linuxカーネルのcontainer_ofマクロを理解するための非常に便利なリンク。 https://linux-concepts.blogspot.com/2018/01/understanding-containerof-macro-in.html