LINUX.ORG.RU

[c][linux][kernel] Не могу понять list_head

 , ,


0

1

Начал глубоко изучать list_head в ядре линукса - наткнулся на непонимание базового принципа работы.

Все макросы list_foreach_* начинают перебор со _второго_ объекта, то есть пропускается самый первый объект.

Это наводит на мысль, что первый объект _всегда_ должен быть «мусорным», то есть он нигде не применяется, а служит только для указания головы списка.

Либо этот самый первый объект должен обрабатываться вручную, что, по-моему, глупо.

Я так понял, что если у меня в программе динамически создаются объекты типа struct my_list_object, которые имеют внутри list_head, то чтобы в _другом_ объекте иметь указатель на этот список, я должен:

1. Либо сделать один «мусорный» объект struct my_list_object, от которого я буду стартовать.

2. Либо иметь указатель на struct my_list_obj * или на соответствующий struct list_head *, который будет равен NULL при пустом списке и иметь нормальное значение при непустом списке.

Мне кажется, что вариант (1) более разумен, однако, смущает наличие мусорного объекта.

Я правильно понимаю «философию» list_head? Если нет, то как надо его применять?

★★

Как пример макроса foreach

/**
 * __list_for_each	-	iterate over a list
 * @pos:	the &struct list_head to use as a loop cursor.
 * @head:	the head for your list.
 *
 * This variant differs from list_for_each() in that it's the
 * simplest possible list iteration code, no prefetching is done.
 * Use this for code that knows the list to be very short (empty
 * or 1 entry) most of the time.
 */
#define __list_for_each(pos, head) \
	for (pos = (head)->next; pos != (head); pos = pos->next)


/**
 * list_for_each_entry	-	iterate over list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#define list_for_each_entry(pos, head, member)				\
	for (pos = list_entry((head)->next, typeof(*pos), member);	\
	     prefetch(pos->member.next), &pos->member != (head); 	\
	     pos = list_entry(pos->member.next, typeof(*pos), member))
bk_ ★★
() автор топика

1й вариант, только не нужен целый «мусорный» объект, нужна лишь структура list_head - указатель на список.

unsigned ★★★★
()

Нда... И эти люди ковыряются в ядре. Ты действительно не знаешь как устроен список, или просто прикалываешься?

Macil ★★★★★
()
Ответ на: комментарий от unsigned

Вот, я тоже так думал, но тогда задался таким вопросом:

у меня есть

struct main_obj {
struct list_head child_objects;
};

struct child_obj {
struct list_head list;
};

struct main_obj m;

Допустим, у меня есть уже несколько child_obj в main_obj.child_objects списке. Я хочу проитерировать по всем child_objects и делаю list_for_each_entry.

#define list_for_each_entry(pos, head, member)            \
   for (pos = list_entry((head)->next, typeof(*pos), member);   \
        prefetch(pos->member.next), &pos->member != (head);    \
        pos = list_entry(pos->member.next, typeof(*pos), member))

НО ТОГДА на последней итерации я сделаю list_entry для main_object->child_objects, и только потом проверю на pos->member != (head)

Это ведь может привести к ошибке?

bk_ ★★
() автор топика
Ответ на: комментарий от tailgunner

Пока нубом выглядишь здесь только ты. Если тямы не хватает нормально ответить, зачем тогда вообще писать?

bk_ ★★
() автор топика
Ответ на: комментарий от Macil

Давай, крутой, объясни, как он работает.

bk_ ★★
() автор топика

>однако, смущает наличие мусорного объекта.

Он не нужен. См. например http://lxr.linux.no/#linux+v2.6.39/include/linux/sched.h#L1294

children — точка входа в список.

siblings — сами head'ы списка (В boost они логично названы «hooks»).

Кстати с прошлого раза когда я смотрел исходники ядра наконец-то добавили документацию о том, какой list_info — точка входа, а какой — сам список. Хорошо бы так было по всему ядру.

elverion
()
Ответ на: комментарий от bk_

> Пока нубом выглядишь здесь только ты

...и только в твоих глазах %)

Если тямы не хватает нормально ответить

Я и нормально ответил.

tailgunner ★★★★★
()
Ответ на: комментарий от Macil

intrusive списки могут показаться странными когда видишь их впервые. Даже если до этого работал с обычными списками.

elverion
()

Харе тут ЧСВ демонстрировать :)
Интересно же :) Давайте по теме :)

Jetty ★★★★★
()
Ответ на: комментарий от bk_

>НО ТОГДА на последней итерации я сделаю list_entry для main_object->child_objects

Не сделаешь. list_for_each пропускает первый элемент.

elverion
()
Ответ на: комментарий от bk_

> Давай, умный, ответь на

Утипути. Родной, ты правда думаешь, что после просьбы в таком тоне я пойду по некликабельной ссылке и начну тебе что-то объяснять?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Утипути. Родной, ты правда думаешь, что после просьбы в таком тоне я пойду по некликабельной ссылке и начну тебе что-то объяснять?

а что ты планируешь делать? дальше поднимать ЧСВ за счет новичка?

aho
()
Ответ на: комментарий от tailgunner

Тогда какого хера ты тут вообще пишешь? Это не толксы, утипути.

bk_ ★★
() автор топика
Ответ на: комментарий от elverion

На _последней_ итерации, не на первой! Я понимаю, что последняя итерация приведет к list_entry с указанием в качестве type main_struct, хотя на самом деле должен быть child_obj.

Если так, что, скорее всего, это может с какой-то вероятностью привести к ошибке обращения к памяти по левому адресу.

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

Последний элемент снова указывает на голову списка. Как только pos на нее попадет, ты вылетишь из цикла по «&pos->member != (head)».

Или я тебя не понял?

unsigned ★★★★
()
Ответ на: комментарий от bk_

Скорее всего обращения к памяти просто не происходит.

Хоть ты и получаешь указатель на мусор, но обращения к этой памяти не происходит. Единственное что известно про этот указатель, что если привести его к типу typeof(*pos) и взять поле member, то там будет корректный list_head.

elverion
()
Ответ на: комментарий от elverion

Посмотри как реализован list_entry, там вообще используется нулевой указатель для получения смещения поля структуры (насколько я помню)

elverion
()
Ответ на: комментарий от unsigned

Вероятно, я не ясно обрисовал ситуацию.

Итак, есть две структуры: главная и дочерняя. Есть список дочерних структур. Доступ к этому списку дочерних структур должен быть у главной структуры

Этот доступ делается через фиктивный list_head, который хранится в главной структуре. Он же представляет собой голову списка, но _никаких данным за ним не хранится_, ибо он лежит в главной структуре.

Итак, я добавил в этот список несколько дочерних структур (понятно, у них есть свой list_head). Теперь я хочу пройтись по этому списку макросом foreach_entry.

Вот здесь для меня возникла загвоздка.

У меня ВСЕ list_head-ы, кроме ПЕРВОГО, лежат в дочерних структурах. Это значит, что в макросе

#define list_for_each_entry(pos, member, head) \
	for (pos = list_entry((head)->next, typeof(*pos), member); \
		&pos->member != (head); \
		pos = list_entry(pos->member.next, typeof(*pos), member))

когда я дойду до реально последнего элемента - последней дочерней структуры (выполнится 3-й блок цикла for), проверка &pos->member != (head) даст TRUE, поскольку это так.

Далее я обработаю своим кодом эту структуру и снова выполнится код 3-го блока цикла for

pos = list_entry(pos->member.next, typeof(*pos), member)

ВОТ ЗДЕСЬ структура по адресу pos->member.next хранится уже в ГЛАВНОЙ структуре, а не в дочерней.

Вот проблема, которую я вижу. Может,я ошибаюсь?

bk_ ★★
() автор топика
Ответ на: комментарий от elverion

Да,я знаю как он реализуется, но сам факт получения невалидного адреса смущает

#define list_entry(ptr, type, member) \
	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

ВОТ ЗДЕСЬ структура по адресу pos->member.next хранится уже в ГЛАВНОЙ структуре, а не в дочерней.

Вот проблема, которую я вижу. Может,я ошибаюсь?

И еще - после получения указателя на struct child_obj в переменную pos - это реально будет strut main_obj *. И тогда проверка во 2ом блоке for даст хрен знает что.

Здесь я запутался.

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

это реально будет strut main_obj *

Это не обязательно будет struct main_obj, но вот такой код:

&((struct child_obj*)pos)->list
обязательно даст указатель на структуру child_objects.

elverion
()
Ответ на: комментарий от elverion

Мне кажется, ты ошибаешься.

pos->list ==> struct list_head

& pos->list ==> struct list_head *

bk_ ★★
() автор топика

Вот как они задумали:

/**
 * list_empty - tests whether a list is empty
 * @head: the list to test.
 */
static inline int list_empty(const struct list_head *head)
{
        return head->next == head;
}
ttnl ★★★★★
()
Ответ на: комментарий от ttnl

Да, именно поэтому я заключил, что должен быть «фиктивный» list_head внутри структуры, которая контолирует списо.

bk_ ★★
() автор топика
Ответ на: комментарий от ttnl

Мог бы ты, пожалуйста, объяснить, как использовать list_head в такой ситуации:

Есть связный список структур struct child_obj. Ясное дело, в ней есть list_head, которым они «соединены.»

Этот linked list должен как-то хранится в структурe struct main_obj и обрабатываться функциями, работающими с этой структурой.

Как лучше всего хранить информацию об этом списке в struct main_obj?

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

>Вот проблема, которую я вижу. Может,я ошибаюсь?

pos = list_entry(pos->member.next, typeof(*pos), member)

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

ttnl ★★★★★
()
Ответ на: комментарий от ttnl

Да, это верно, я уже поговорил с rostedt на irc://kernelnewbees.

Бл__ь, как я сразу не понял про одинаковые сдвиги...

Однако остается открытым вопрос - этот «неизвестный» сдвиг от &main_obj.child_list - SMTH не вызовет ошибок обращения к памяти, например, при отсутствии доступа?

bk_ ★★
() автор топика
Ответ на: комментарий от bk_
все же просто
list_head zveri;
for (list_head* pos = zveri.next;
pos != &zveri; pos = pos->next)
или
struct mega{list_head zveri;};
mega zoopark;
for (list_head* pos = zoopark.zveri.next;
pos != &zoopark.zveri; pos = pos->next)
anonymous
()
Ответ на: комментарий от bk_

чудесные творения эти списки подкупают своей простотой и мощью но всеже для обычных дел я бы взял std::list или GList

anonymous
()
Ответ на: комментарий от bk_

ну во первых не надо юзерские структуры портачить витыкая в них list_head ы линее телодвижение лень плюс std::list сам память выделяет к тому же тупо писанины меньше ну а GList хотя бы не требует специальных структур со связями внутри опять же телодвижений меньше

anonymous
()
Ответ на: комментарий от anonymous

Зато самый главный недостаток - неЪ.

bk_ ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.