Подтвердить что ты не робот

Понимание макроса 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);

который выглядит как ничего не делать. Может ли кто-нибудь заполнить пустоту здесь?

4b9b3361

Ответ 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 в основном используется в универсальных контейнерах в ядре.

Это все о контейнере макроса в ядре.