Понимание макроса container_of в ядре Linux

Когда я просматривал kernel ​​Linux, я обнаружил макрос container_of который определяется следующим образом:

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

Я понимаю, что делает контейнер, но я не понимаю, это последнее предложение, которое

 (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 в Documentation/rbtree.txt , в коде ядра Linux это не 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, ie 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 .
  • поскольку struct является блоком смежной памяти, адрес obj.vm_rb минус offset between the struct and member будет адресом контейнера.

include/linux/kernel.h:858include/linux/kernel.h:858 container_of

Определение include/linux/rbtree.h:51rb_entry

mm/mmap.c:299 – использование rb_entry

include/linux/mm_types.h:284struct vm_area_struct

Documentation/rbtree.txt: – Документация красно-черного дерева

Определение include/linux/rbtree.h:36struct rb_node

PS

Выше файлы находятся в текущей версии разработки, то есть 4.13.0-rc7 .

file:k означает kth строку в file .

  • Radeon HD6570 работает только в ядре Linux 3.6.7
  • В чем разница между module_init и subsys_initcall при инициализации драйвера?
  • Dkms не находит источник ядра
  • Каково теоретическое максимальное количество открытых TCP-соединений, которые может иметь современный ящик Linux
  • Не удалось установить VIrtualbox, `" Определить KERN_DIR = <каталог> "` - установить Virtual Box в Debian?
  • Что делает request_mem_region () на самом деле и когда это необходимо?
  • функция cat вызывает read () бесконечное время
  • В чем разница между vmalloc и kmalloc?
  • Какова функция «(void) (& _min1 == & _min2)» в мини-макросе в kernel.h?
  • Как использовать сокет netlink для связи с модулем ядра?
  • Обоснование макроса container_of в linux / list.h
  • Давайте будем гением компьютера.