developer tip

Linux 커널의 container_of 매크로 이해

optionbox 2020. 10. 30. 07:56
반응형

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

아무것도하지 않는 것 같습니다. 아무도 여기서 공백을 채울 수 있습니까?


container_of(dev, struct wifi_device, dev);두 개의 네임 스페이스를 혼합 할 때 사용 예제 가 약간 오해의 소지가있을 수 있습니다.

dev예제 의 첫 번째 는 포인터의 이름을 참조하고 두 번째 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 확장, 문 표현식 의 활용입니다 . 매크로가 값을 반환하는 것으로 표시되면 마지막 줄은 다음과 같습니다.

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리눅스 커널 코드의 상태, 그것은 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 is a pointer pointing member of an type instance, here rb_node *nd.

what container_of do is, as in this example,

  • given address of obj.member (here obj.vm_rb), return the address of obj.
  • since a struct is a block of contiguous memory, address of obj.vm_rb minus offset between the struct and member will be the container's address.

include/linux/kernel.h:858 -- definition of container_of

include/linux/rbtree.h:51 -- definition of rb_entry

mm/mmap.c:299 -- usage of rb_entry

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

Documentation/rbtree.txt: -- Documentation of red-black tree

include/linux/rbtree.h:36 -- definition of struct rb_node

P.S.

Above files are in current develop version, i.e, 4.13.0-rc7.

file:k mean kth line in file.


conatainer_of() macro in Linux Kernel -

When it comes to managing several data structures in code, you'll almost always need to embed one structure into another and retrieve them at any moment without being asked questions about memory offsets or boundaries. Let's say you have a struct person, as defined here:

 struct person { 
     int age; 
     int salary;
     char *name; 
 } p;

By only having a pointer on age or salary, you can retrieve the whole structure wrapping (containing) that pointer. As the name says, the container_of macro is used to find the container of the given field of a structure. The macro is defined in include/linux/kernel.h and looks like the following:

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

Don't be afraid of the pointers; just see them as follows:

container_of(pointer, container_type, container_field); 

Here are the elements of the preceding code fragment:

  • pointer: This is the pointer to the field in the structure
  • container_type: This is the type of structure wrapping (containing) the pointer
  • container_field: This is the name of the field to which pointer points inside the structure

Let's consider the following container:

struct person { 
    int age; 
    int salary; 
    char *name; 
}; 

Now, let's consider one of its instances, along with a pointer to the age member:

struct person somebody; 
[...] 
int *age_ptr = &somebody.age; 

Along with a pointer to the name member (age_ptr),you can use the container_of macro in order to get a pointer to the whole structure (container) that wraps this member by using the following:

struct person *the_person; 
the_person = container_of(age_ptr, struct person, age); 

container_of takes the offset of age at the beginning of the struct into account to get the correct pointer location. If you subtract the offset of the field age from the pointer age_ptr, you will get the correct location. This is what the macro's last line does:

(type *)( (char *)__mptr - offsetof(type,member) ); 

Applying this to a real example, gives the following:

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

The container_of macro is mainly used in generic containers in the kernel.

That's all about container_of macro in kernel.


Very useful link for understanding container_of macro in linux kernel. https://linux-concepts.blogspot.com/2018/01/understanding-containerof-macro-in.html

참고URL : https://stackoverflow.com/questions/15832301/understanding-container-of-macro-in-the-linux-kernel

반응형