Linuxカーネルでのcontainer_ofマクロの理解


Answers

最後の文のキャスト:

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

このオフセットを与えられたポインタから減算する。 結果は構造体の先頭へのポインタです。 最後に、このポインタを有効な変数を持つ構造体型にキャストします。

Question

私が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を理解する方法です。

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_entryrb_entryと同じです。

mm/mmap.c:299で関数定義browse_rb中にrb_entry使用法があり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 instanceのメンバの名前です。ここではvm_rbです。タイプはrb_node
  • ptrtypeインスタンスのポインター・ポインター・member 、ここではrb_node *ndです。

この例のように、 container_of doは、

  • 与えられた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:kfile:k番目の行を意味しfile




Links