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


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

740   4  

4 ответов:

ваш пример использования 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 состояния, в коде ядра 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 сделать это, как в данном примере,

  • данный адрес 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.

Comments

    Ничего не найдено.