Розуміння макросу container_of у ядрі Linux


84

Коли я переглядав ядро ​​Linux, я знайшов container_ofмакрос, який визначається наступним чином:

Я розумію, що робить container_of, але що я не розумію, це останнє речення, яке є

Якщо ми використовуємо макрос наступним чином:

Відповідною частиною останнього речення буде:

що виглядає як нічого не робити. Хтось міг заповнити порожнечу тут?


1
ця відповідь має реальний та інтуїтивно зрозумілий приклад використання червоно-чорного дерева rb_node.
qeatzy

Відповіді:


87

Ваш приклад використання container_of(dev, struct wifi_device, dev);може трохи ввести в оману, оскільки ви змішуєте там два простори імен.

У той час як перший devу вашому прикладі посилається на ім'я вказівника, другий dev- на ім'я елемента структури.

Швидше за все це змішування провокує весь головний біль. Насправді memberпараметр у вашій пропозиції посилається на ім'я, дане цьому члену в структурі контейнера.

Взявши цей контейнер, наприклад:

І вказівник int *my_ptrна this_dataчлен, до якого ви б використали макрос, щоб отримати вказівник struct container *my_container, використовуючи:

Врахування зміщення this_dataдо початку структури має важливе значення для отримання правильного розташування вказівника.

Фактично вам просто потрібно відняти зміщення елемента this_dataвід вашого вказівника, my_ptrщоб отримати правильне розташування.

Це саме те, що робить останній рядок макросу.


5
Для тих з вас, хто потребує більш детального пояснення: Радек Паздера у своєму блозі дуже чітко пояснив container_of macro ( include/linux/kernel.h) . До речі : list_entry макрозйомки ( ) використовується для бути визначені дуже так само , але тепер визначається як . include/linux/list.hcontainer_of
patryk.beza

1
Також цей допис у блозі Грега Кроаха-Хартмана може бути корисним: kroah.com/log/linux/container_of.html
EFraim

1
В принципі, це приємна відповідь, за винятком того, що вона насправді не відповідає на значну частину питання? Наприклад, чого досягає останній рядок коду і як?
Michael Beer

Коли я слідую цьому прикладу, використовуючи GCC, я отримую error: initialization from incompatible pointer type. Це пов'язано з constвизначенням макросу відповідно до цього потоку? stackoverflow.com/a/39963179/1256234
Andy J

18

Останнє речення:

вказівник на задану type. Покажчик обчислюється як зміщення від заданого покажчика dev:

Коли ви використовуєте cointainer_ofмакрос, ви хочете отримати структуру, що містить вказівник на дане поле. Наприклад:

У вас є вказівник, який вказує на середину структури (і ви знаєте, що це вказівник на поле two[ ім’я поля в структурі ]), але ви хочете отримати всю структуру ( numbers). Отже, ви обчислюєте зміщення поданої twoв структурі:

і відніміть це зміщення від заданого покажчика. Результат - вказівник на початок структури. Нарешті, ви призначаєте цей покажчик до типу структури, щоб мати дійсну змінну.


10

Це використання розширення gcc, вирази операторів . Якщо ви бачите макрос як щось, що повертає значення, то останній рядок буде таким:

Див. Посилання на сторінку для пояснення складених тверджень. Ось приклад:

Вихідний результат

b 25


7

макрос conatainer_of () у ядрі Linux -

Коли справа доходить до управління кількома структурами даних у коді, вам майже завжди потрібно буде вбудовувати одну структуру в іншу та отримувати їх у будь-який момент, не задаючи питань про зміщення пам’яті чи межі. Скажімо, у вас є структура людини, як визначено тут:

Маючи лише вказівник на вік або зарплату, ви можете отримати всю структуру, яка обертає (містить) цей вказівник. Як випливає з назви, макрос container_of використовується для пошуку контейнера заданого поля структури. Макрос визначений у include / linux / kernel.h і виглядає так:

Не бійтеся покажчиків; просто перегляньте їх наступним чином:

Ось елементи попереднього фрагмента коду:

  • покажчик: це вказівник на поле в структурі
  • container_type: Це тип обтікання структури (що містить) покажчик
  • container_field: Це ім'я поля, на яке вказує вказівник всередині структури

Давайте розглянемо наступний контейнер:

Тепер давайте розглянемо один із його випадків, разом із вказівником на віковий член:

Поряд з покажчиком на ім'я члена (age_ptr), ви можете використовувати макрос container_of, щоб отримати вказівник на всю структуру (контейнер), яка обертає цього члена, використовуючи наступне:

container_of враховує зміщення віку на початку структури, щоб отримати правильне розташування вказівника. Якщо відняти зміщення віку поля від покажчика age_ptr, ви отримаєте правильне розташування. Ось що робить останній рядок макросу:

Застосовуючи це до реального прикладу, можна отримати наступне:

Макрос container_of в основному використовується в загальних контейнерах ядра.

Це все про макрос container_of в ядрі.


3

Трохи реальний контекст говорить ясніше, нижче використовуйте червоно-чорне дерево як приклад , який я розуміюcontainer_of .

як Documentation/rbtree.txtзазначено, в коді ядра Linux це, швидше, не rb_node, що містить введення даних

Вузли даних у дереві rbtree - це структури, що містять член struct rb_node.

struct vm_area_struct (у файлі include/linux/mm_types.h:284 ) - це така структура,

у тому ж файлі є макрос, rb_entryякий визначається як

ясно, rb_entryце те саме, що container_of.

у mm/mmap.c:299внутрішньому визначенні функції browse_rbвикористовується rb_entry:

тепер зрозуміло, в 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

PS

Наведені вище файли знаходяться в поточній версії для розробки, тобто 4.13.0-rc7.

file:kсередній k-й рядок в file.



0

Найпростіша реалізація контейнера _ макросу нижче, це зменшує всі складні перевірки типу та роботи

ptr дасть адресу учасника і просто зменшить різницю зміщення, і ви отримаєте стартову адресу.

Приклад використання

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.