2014-03-28 2 views
3

Почему мы используем макрос container_of?Сомнения в отношении контейнера macro в linux

container_of(pointer, container_type, container_field); 

Говорят, в LDD, что

«Этот макрос принимает указатель на поле с именем container_field, в структуру типа container_type, и возвращает указатель на , содержащей структуру» ,

Мои вопросы:

  • Если мы хотим, чтобы указатель на структуру (т.е. container_type) мы можем непосредственно назначенная правильно?
  • Тогда почему указатель одного из его полей был присвоен адрес для всей структуры?
  • Может ли кто-нибудь объяснить в примере преимущество использования того макроса?
+0

По причине это техническое обслуживание. Если все используют макрос, вы можете легко изменить способ назначения указателя во всем проекте, просто отредактировав макрос. –

ответ

3

Позвольте мне дать вам пример:

struct A 
{ 
    int some_data; 
    int other_data; 
}; 

Теперь давайте сказать, что есть эта функция:

int get_some_data_from_other(int *other) 
{ 
    struct A *a = container_of(other, struct A, other_data); 
    return a->some_data; 
} 

Как вы можете видеть, мы можем сказать, что это оригинальный struct A, который содержит данный int *other, зная, в каком поле структуры указывается, на что указывает указатель. Ключ здесь в том, что мы не делаем имеем ссылку на собственно структуру, а просто указатель на один из ее членов.

Это может показаться абсурдным, но на самом деле полезно в некоторых очень умных конструкциях. Один очень распространенный пример - это то, как ядро ​​создает связанные списки. Предлагаю вам прочитать this post on kernelnewbies.org. Давайте посмотрим, короткий пример этого:

struct whatever 
{ 
    /* whatever data */ 
    struct list_head mylist; 
}; 

Так struct whatever есть некоторые данные, но он также хочет, чтобы действовать в качестве узла внутри связанного списка. То, чему они учат вас в школе, - это иметь другую структуру, содержащую указатели next/prev, а также указатель на struct whatever (или void *). Таким образом, у вас есть узлы, через которые вы получаете свои данные.

По всем стандартам разработки программного обеспечения это на самом деле хорошо. Но стандарты разработки программного обеспечения не имеют отношения к эффективности w.r.t. См. Why should I have written ZeroMQ in C, not C++ (part II).

Нижняя строка с помощью обычного метода выделяет узел связанного списка отдельно от узла данных, т. Е. Вы удваиваете накладные расходы на распределение памяти, освобождение, фрагментацию и пропуски кэша среди других. То, как это делает ядро ​​Linux, противоположно. Каждый узел данных содержит общий узел связанного списка. Общий узел связанного списка ничего не знает о данных или о том, как они были распределены, и знает только, как подключиться к другим связанным узлам списка.

Итак, давайте более глубокий взгляд:

+-------------------+  +---------------------+  +---------------------+ 
|     |  |      |  |      | 
| WHATEVER DATA |  | WHATEVER DATA 2 |  | WHATEVER DATA 3 | 
|     |  |      |  |      | 
|     |  |      |  |      | 
|     |  |      |  |      | 
|     |  |      |  |      | 
+-------------------+  +---------------------+  +---------------------+ 
|     |----->|      |----->|      | 
|  mylist  |  |  mylist 2  |  |  mylist 3  | 
|     |<-----|      |<-----|      | 
+-------------------+  +---------------------+  +---------------------+ 

Что вы имеете в связанном списке являются указателями внутри struct list_head, которые указывают на другие struct list_head с. Обратите внимание, что они не указывают на struct whatever, но на mylistвнутри эти структуры.

Скажите, что у вас есть узел, struct whatever w. Вы хотите найти следующий узел. Что ты можешь сделать? Во-первых, вы можете сделать w.mylist.next, чтобы получить указатель на mylist следующего узла. Теперь вы должны иметь возможность извлечь фактический struct whatever, содержащий этот узел. Вот где container_of используется:

struct whatever w_next = container_of(w.mylist.next, struct whatever, mylist); 

Наконец, следует отметить, что ядро ​​Linux имеет макросы для прохождения по всем узлам связанного списка, который чаще всего не то, что вы хотите, так что вы на самом деле не нужно используйте container_of самостоятельно.

1
Why do we use container_of macro ? 

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

Например

struct container { 
    int some_other_data; 
    int this_data; 
} 

и указатель int *my_ptr к this_data элемента нужно использовать макрос, чтобы получить указатель на структуру контейнера *my_container с помощью:

struct container *my_container; 
my_container = container_of(my_ptr, struct container, this_data); 

Принимая смещение this_data к началу структуры учитывается, чтобы получить правильное расположение указателя.

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

Также см. here, которые очищают ваши сомнения.

+0

Декларация 'container container * my_container' будет указывать неявно на начало структуры правильно? Тогда почему это явное объявление? – ddpd

+0

Да, но вы можете вычесть смещение члена. –

+0

@Dino 'struct container * my_container' не инициализируется. Он нигде не указывает. Все, что у нас есть, - это указатель на 'int'. Если мы знаем, что int является членом 'struct container', мы можем восстановить указатель на' struct container'. – nos

0

Да, вы можете использовать для этого свой собственный листинг/присвоение, но он имеет некоторые преимущества.

Эффективность - Не проще просто использовать макрос, а не объявлять весь литой?

Техническое обслуживание. Если все используют макрос, вы можете легко изменить способ назначения указателя во всем проекте, просто отредактировав макрос.

Читаемость - что легко читать? явный листинг или макрос, который делает это за вас?

Безопасность. Вы можете доверять тому, что этот макрос работает, и что он выполняет любые проверки при проверке и типе, которые важны, намного лучше, чем ваш собственный код.

Смежные вопросы