Container_of
Container_of는 리눅스 커널에서 매우 유용하게 자주 쓰이는 매크로로 구조체 멤버의 포인터로부터 해당 멤버가 속한 구조체의 시작 주소를 얻는데 사용된다.
Container_of 매크로의 정의는 다음과 같으며, 인자로 '구조체 멤버의 포인터', '전체 구조체의 타입', '구조체 멤버의 이름'을 받고 '전체 구조체의 시작 주소'를 반환한다.
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
static_assert(__same_type(*(ptr), ((type *)0)->member) || \
__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })
Container_of 구현
#define container_of(ptr, type, member) \
((type *)((char *)(ptr) - (size_t)&(((type *)0)->member)))
동작 원리는다음과 같다.
- (type *)0 : 구조체의 시작 주소를 0으로 가정한다.
- (size_t)&(((type *)0)->member) : 멤버의 주소를 정수형으로 반환한다. 가상 주소 0에서 시작하는 구조체의 멤버의 위치는 구조체 시작 주소와 멤버 주소 사이의 바이트 오프셋과 동일하다. 이 과정은 offsetof() 매크로와 동일한 동작을 수행한다.
- ((char *)(ptr) - (size_t)&(((type *)0)->member)) : 인수로 받은 멤버 주소에서 바이트 오프셋을 빼서 전체 구조체의 시작 주소를 얻는다.
- ((type *)((char *)(ptr) - (size_t)&(((type *)0)->member))) : 마지막으로 type *로 캐스팅하고 반환한다.
사용 예시.
#include <stdio.h>
struct student {
short id;
const char *name;
int age;
};
#define container_of(ptr, type, member) \
((type *)((char *)(ptr) - (size_t)&(((type *)0)->member)))
void foo(const char **name_ptr) {
struct student *ps = container_of(name_ptr, struct student, name);
printf("Using container_of:\n");
printf("ID: %d, Name: %s, Age: %d\n", ps->id, ps->name, ps->age);
}
int main() {
struct student s = {1, "Alice", 20};
foo(&s.name);
return 0;
}
foo 함수에서는 구조체의 멤버의 주소를 인수로 받아서 이를통해 전체 구조체에 접근하는 예시이다.
아래와 같이 구조체를 생성하고 직접 오프셋을 계산하는 방법도 있다. 다만 이런 방식은 매크로 함수와 달리 구조체 이름과 멤버 이름을 매개변수로 받을 수 없으므로 함수화는 어렵다.
struct student s;
size_t offset = (char *)&s.name - (char *)&s;
struct student *ps = (struct student *)((char *)name_ptr - offset);
printf("Using container_of:\n");
printf("ID: %d, Name: %s, Age: %d\n", ps->id, ps->name, ps->age);
'임베디드 개발 > 리눅스 디바이스 드라이버' 카테고리의 다른 글
Linux Kernel ] Error Handling (0) | 2025.02.03 |
---|---|
Device tree compiler 사용법 (0) | 2025.01.27 |
LDD ] PCIe 디바이스 드라이버 작성하기 - (2) (0) | 2025.01.15 |
VSCode에서 리눅스 커널 모듈 개발시 Intellisense Error 없애기 (0) | 2024.12.29 |
LDD ] PCIe 디바이스 드라이버 작성하기 - (1) (0) | 2024.12.25 |