ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • kernel macro, container_of
    2013. 1. 11. comments
    반응형

    container_of 란?

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

    container_of는 커널 매크로로써, 구조체의 멤버변수의 주소값만 알고 있다면 그 구조체 주소를 얻어 올 수 있는 방법입니다. 세가지 인자를 갖습니다.

    1. 멤버변수의 주소값(ptr)

    2. 구조체 type(struct)

    3. 멤버변수 이름(member)


    이 세가지 정보를 가지고 구조체에 해당하는 주소를 가져오는데요. 원리는 간단합니다.

    일단, 해당 멤버변수가 구조체 시작 위치에서 어디에 위치하는지 offset정보를 구합니다.

    offsetof(type,member);



    멤버변수의 주소값(메모리상 어딘가에 위치하겠죠)에서 offset을 빼게 되면 struct의 시작 주소가 될것입니다. 그걸 type 캐스팅을 해주기만 하면 됩니다.

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


    간단예제 1.

    struct abc
    {
      int a;
      int b;
      int c;
    };
    


    실행 중, c의 주소값만 알고 있다고 해도, struct abc 전체에 접근할 수 있습니다.





    간단 예제2.

    struct object
    {
        unsigned int id;
    };
    
    struct meeting
    {
        struct object id;
        int callPhoneNumber;
    };
    
    int callMeetingPerson(struct object* obj)
    {
        struct meeting* meet = container_of(obj, struct meeting, id);
        ...
        ...
    }
    
    

    이렇게, 구조체 object를 넘겨받은 callMeetingPerson 함수지만 실제 처리는 meeting 구조체를 가지고 하는 것입니다. container_of 함수에서 두번째 인자를 보면 meeting 구조체라는 것을, 그리고 세번째 인자에서 struct object의 멤버 변수 이름을 넣어주었습니다. 이 정보를 가지고 offset을 구하고 첫번째 인자에서 받은 주소값에서 offset을 빼고 meeting 구조체로 캐스팅되어서 meet이란 변수에 주소값이 문제없이 들어가게 됩니다. 


    만약 struct object를 다른 구조체에 넣어두고 이 함수에서 호출을 하면 분명 런타임 오류가 발생합니다. 잘못된 주소에 접근할 수 있기 때문인데요. 간단한 예제지만 활용하려면 견고한 설계가 필요합니다.



    kernel list 구조체

    이런 유연성 때문에  linux kernel에서는 여러 자료구조에서 사용되는데, 대표적인 경우가 리스트 자료구조입니다. 


    struct list_head {

        struct list_head *next, *prev;

    };


    #define LIST_HEAD_INIT(name) { &(name), &(name) }


    #define LIST_HEAD(name) \

        struct list_head name = LIST_HEAD_INIT(name)


    static inline void INIT_LIST_HEAD(struct list_head *list)

    {

        list->next = list;

        list->prev = list;

    }


    /**    
     * list_for_each_safe - iterate over a list safe against removal of list entry
     * @pos:    the &struct list_head to use as a loop cursor.
     * @n:      another &struct list_head to use as temporary storage
     * @head:   the head for your list.
     */    
    #define list_for_each_safe(pos, n, head) \
        for (pos = (head)->next, n = pos->next; pos != (head); \
            pos = n, n = pos->next)


    /**    
     * list_for_each_entry  -   iterate over list of given type
     * @pos:    the type * to use as a loop cursor.
     * @head:   the head for your list.
     * @member: the name of the list_struct within the struct.
     */
    #define list_for_each_entry(pos, head, member)              \
        for (pos = list_entry((head)->next, typeof(*pos), member);  \
             prefetch(pos->member.next), &pos->member != (head);    \
             pos = list_entry(pos->member.next, typeof(*pos), member))

    /**
     * list_entry - get the struct for this entry
     * @ptr:    the &struct list_head pointer.
     * @type:   the type of the struct this is embedded in.
     * @member: the name of the list_struct within the struct.
     */
    #define list_entry(ptr, type, member) \
        container_of(ptr, type, member)

    리스트로 구성할 struct안에 list_head struct를 포함시킵니다. 그리고 연결할 여러개의 데이터들을 list_add 등 제공되는 함수를 통해 연결시킵니다. 그 함수들의 인자는 list_head struct만 받기 때문에 list_head가 어떤 struct에 포함되어 있는지는 관여하지 않습니다. 왜냐하면 결국, container_of를 통해서 list_head가 포함된 struct를 접근할 수 있기 때문입니다. container_of는 list_entry로 재정의하기 때문에 list_head에서는 list_entry를 사용합니다.


    반응형

    댓글

Designed by Tistory.