Ответ 1
EDIT: извините, должно быть, поздно, я сделал много опечаток.
Они просто весело!:) Разница в том, что list_for_each_entry
сломается, если вы удалите что-то во время итерации списка, а list_for_each_entry_safe
не будет (конечно, за счет некоторых дополнительных инструкций CPU).
Ядро оговорилось на двусвязных списках (которые, как я полагаю, вы понимаете), хотя в list.h имеется реализация с одним списком ссылок. Ваш список просто:
struct list_head {
struct list_head *next;
struct list_head *prev;
};
Обратите внимание, что одна и та же структура используется как для "головки" списка, так и для каждого node. Когда список пуст, члены головы next
и prev
просто указывают на его голову. Таким образом, повторение списка - это просто процесс запуска с элемента head next
и вызов этого node, если только тот же адрес, что и prev
(при остановке). В противном случае тело for
вызывается, и вы можете использовать макрос container_of()
, чтобы получить указатель на свою фактическую структуру и играть с ним. Затем, в третьем поле for
, мы просто перейдем к следующему next
.
ИЗМЕНИТЬ: крики, прошу прощения, вы попросили объяснить параметры. Хорошо, я бы проверял это прямо, если бы я был вами, а не заставлял кого-то говорить об этом. Для них я бы предложил Kernel API docs, которые, по крайней мере, существуют для библиотеки связанных списков. Я пытаюсь получить набор патчей, который добавит их для библиотеки с красно-черным деревом, но получение информации через процесс может быть довольно сложным.
Также обратите внимание: http://kernelnewbies.org/FAQ/LinkedLists
Вот пример:
struct list_head my_actual_list;
struct my_struct {
struct list_head node;
/* some other members */
};
/* in a function body somewhere... */
struct list_head *i;
list_for_each(i, &my_actual_list) {
struct my_struct *obj = list_entry(i, struct my_struct, node);
// do something with obj
}
list_entry
является просто псевдонимом для container_of
РЕДАКТИРОВАТЬ № 2
ОК, поэтому, отвечая на ваш вопрос в комментариях, я собираюсь просто расширить свой ответ. Я действительно могу оценить трудности в понимании этой концепции, поскольку в ней есть несколько странных вещей в сравнении с контейнерами С++ STL, массивами C и т.д., Но как только вы привыкнете к идиомам, это будет казаться вполне естественным. Еще в будущем я действительно призываю вас начать изучать определение этих структур, функций и макросов самостоятельно и пытаться собрать вместе понимание, а затем задавать вопросы.
Итак, каждый node в вашем списке представляет собой структуру, содержащую элемент типа struct list_head
, а список его self имеет тип struct list_head
. Таким образом, кто является контейнером и кто является содержащимся в этом случае, просто зависит от того, как они используются, но, как правило, он будет выражаться в именах этих членов. Тип итератора - struct list_head *
. Вот пример, и я заменил нормальную функцию и вызовы макросов с их эквивалентным кодом:
struct my_container {
struct list_head list;
int some_member;
/* etc. */
};
struct my_obj {
struct list_head node;
int some_member;
/* etc. */
};
void func() {
struct my_container container;
struct my_obj obj1, obj2;
struct list_head *i;
/* INIT_LIST_HEAD(&container.list); */
container.list.next = &container.list;
container.list.prev = &container.list;
/* list_add_tail(&obj1.node); */
container.list.prev = &obj1.node;
obj1.node.next = &container.list;
obj1.node.prev = &container.list;
container.list.next = &obj1.node;
/* list_add_tail(&obj2.node); */
container.list.prev = &obj2.node;
obj2.node.next = &container.list;
obj2.node.prev = &obj1.node;
obj1.node.next = &obj2.node;
/* list_for_each(i, &container.list) { */
for (i = container.list.next; i != &container.list; i = i->next) {
struct my_obj *obj = list_entry(i, struct my_obj, node);
/* do stuff */
}
}