web-dev-qa-db-ja.com

Linuxカーネルのcontainer_ofマクロについて

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

何もしないように見えます。誰でもここの空所を埋めていただけますか?

67
jaeyong

使用例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のオフセットを差し引くだけです。

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

75
mikyra

最後の文キャスト:

(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)

指定されたポインタからこのオフセットを減算します。結果は、構造の開始点へのポインターです。最後に、このポインターを構造型にキャストして、有効な変数を取得します。

16
Federico

これは、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

8
shodanex

例として赤黒木を使用、これは私が_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_
  • 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_の定義

P.S。

上記のファイルは現在の開発バージョン、つまり_4.13.0-rc7_にあります。

_file:k_は、fileのk行目を意味します。

2
qeatzy

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

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

  • ポインター:これは、構造内のフィールドへのポインターです
  • 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マクロのすべてです。

1
Upen

Linuxカーネルのcontainer_ofマクロを理解するための非常に便利なリンク。 https://linux-concepts.blogspot.com/2018/01/understanding-containerof-macro-in.html

0
Anand Kumar