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
답변
Linux 커널의 conatainer_of () 매크로-
코드에서 여러 데이터 구조를 관리 할 때 거의 항상 한 구조를 다른 구조에 포함하고 메모리 오프셋이나 경계에 대한 질문없이 언제든지 검색해야합니다. 여기에 정의 된대로 구조체 사람이 있다고 가정 해 보겠습니다.
struct person {
int age;
int salary;
char *name;
} p;
나이 또는 급여에 대한 포인터 만 있으면 해당 포인터를 포함하는 전체 구조를 검색 할 수 있습니다. 이름에서 알 수 있듯이 container_of 매크로는 구조의 주어진 필드의 컨테이너를 찾는 데 사용됩니다. 매크로는 include / linux / kernel.h에 정의되어 있으며 다음과 같습니다.
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
포인터를 두려워하지 마십시오. 다음과 같이 참조하십시오.
container_of(pointer, container_type, container_field);
다음은 이전 코드 조각의 요소입니다.
- 포인터 : 구조의 필드에 대한 포인터입니다.
- container_type : 포인터를 감싸는 (포함) 구조의 유형입니다.
- container_field : 포인터가 구조 내부를 가리키는 필드의 이름입니다.
다음 컨테이너를 고려해 보겠습니다.
struct person {
int age;
int salary;
char *name;
};
이제 나이 멤버에 대한 포인터와 함께 인스턴스 중 하나를 고려해 보겠습니다.
struct person somebody;
[...]
int *age_ptr = &somebody.age;
이름 멤버 (age_ptr)에 대한 포인터와 함께 다음을 사용하여이 멤버를 래핑하는 전체 구조 (컨테이너)에 대한 포인터를 가져 오기 위해 container_of 매크로를 사용할 수 있습니다.
struct person *the_person;
the_person = container_of(age_ptr, struct person, age);
container_of는 올바른 포인터 위치를 얻기 위해 구조체 시작 부분의 age 오프셋을 고려합니다. 포인터 age_ptr에서 필드 age의 오프셋을 빼면 올바른 위치를 얻을 수 있습니다. 이것은 매크로의 마지막 줄이하는 일입니다.
(type *)( (char *)__mptr - offsetof(type,member) );
이것을 실제 예제에 적용하면 다음이 제공됩니다.
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);
container_of 매크로는 주로 커널의 일반 컨테이너에서 사용됩니다.
이것이 커널의 container_of 매크로에 관한 것입니다.
답변
약간의 실제 컨텍스트는 더 명확하다고 말합니다. 아래에서 빨간색-검정색 트리를 예로 사용 하십시오 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
member
회원의 이름type
예, 여기vm_rb
유형이다rb_node
,ptr
여기member
에서type
인스턴스를 가리키는 포인터rb_node *nd
입니다.
무엇을 container_of
할 것은,이 예에서와 같이,이다
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
추신
위의 파일은 현재 개발 버전 4.13.0-rc7
입니다.
file:k
에서 k 번째 줄을 의미합니다 file
.
답변
Linux 커널에서 container_of 매크로를 이해하는 데 매우 유용한 링크입니다.
https://linux-concepts.blogspot.com/2018/01/understanding-containerof-macro-in.html
답변
Container _of 매크로의 가장 간단한 구현은 다음과 같습니다.
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))
ptr은 멤버의 주소를 제공하고 오프셋 차이를 빼면 시작 주소를 얻습니다.
사용 예
struct sample {
int mem1;
char mem2;
int mem3;
};
int main(void)
{
struct sample sample1;
printf("Address of Structure sample1 (Normal Method) = %p\n", &sample1);
printf("Address of Structure sample1 (container_of Method) = %p\n",
container_of(&sample1.mem3, struct sample, mem3));
return 0;
}