-
반응형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를 사용합니다.반응형