Понимание макроса container_of в ядре Linux
Когда я просматривал ядро 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);
который выглядит как ничего не делать.
Может ли кто-нибудь заполнить пустоту здесь?
Ответы
Ответ 1
Пример использования 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
, чтобы получить правильное местоположение.
То, что делает последняя строка макроса.
Ответ 2
Последнее предложение:
(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);
У вас есть указатель, который указывает в середине структуры (и вы знаете, что это указатель на filed two
[имя поля в структуре]), но вы хотите получить всю структуру (numbers
). Итак, вы вычисляете смещение поданной two
в структуре:
offsetof(type,member)
и вычтите это смещение от заданного указателя. Результатом является указатель на начало структуры. Наконец, вы указали этот указатель на тип структуры, чтобы иметь допустимую переменную.
Ответ 3
Это использование расширения 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
Ответ 4
Маленький реальный контекст говорит более четко, ниже используется красно-черное дерево в качестве примера, которое является
который я понимаю container_of
.
как Documentation/rbtree.txt
указывает, что в коде ядра linux он не rb_node содержит данные
запись, скорее
Узлы данных в дереве rbtree представляют собой структуры, содержащие структуру 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
.
at 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
do, как в этом примере,
- заданный адрес
obj.member
(здесь obj.vm_rb
), верните
адрес obj
.
- поскольку struct представляет собой блок смежной памяти, адрес
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
P.S.
Выше файлы находятся в текущей версии разработки, т.е. 4.13.0-rc7
.
file:k
означает k-ю строку в file
.
Ответ 5
макрос conatainer_of() в ядре Linux -
Когда дело доходит до управления несколькими структурами данных в коде, вам почти всегда нужно встраивать одну структуру в другую и извлекать их в любой момент, не задавая вопросов о смещениях или границах памяти. Допустим, у вас есть структура, как определено здесь:
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_ptr, вы получите правильное местоположение. Вот что делает последняя строка макроса:
(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 в основном используется в универсальных контейнерах в ядре.
Это все о контейнере макроса в ядре.
Ответ 6
Очень полезная ссылка для понимания макроса container_of в ядре Linux. https://linux-concepts.blogspot.com/2018/01/understanding-containerof-macro-in.html