[Из песочницы] Как использовать список ядра Linux для создания очереди
Приветствую! В данной статье рассматривается использование реализации двусвязного списка ядра Linux.
Двусвязный список в ядре Linux реализован в файле include/linux/list.h. Мы будем использовать адаптированный вариант list.h [1], который отличается от оригинального возможностью использовать его в userspace. Например, создадим очередь — структурe данных с доступом к элементам по принципу «первый пришёл — первый вышел» для произвольного типа данных на основе list.h.Для этого создаем структуру очереди и структуру элемента очереди:
Файл fifo.h:
#ifndef FIFO_H #define FIFO_H
#include «list.h»
struct fifo_item { void* data; // указатель на произвольные данные struct list_head list; // структура двусвязного списка };
struct fifo { struct list_head headlist; // двусвязный список int length; // длина void (*item_free)(struct fifo_item*); // метод освобождения памяти при удалении элемента };
// создание и удаление int fifo_init (struct fifo * fifo, void (*item_free)(struct fifo_item*)); int fifo_exit (struct fifo * fifo); // добавление и извлечение данных int fifo_push (struct fifo * fifo, void* data); void* fifo_pop (struct fifo * fifo); // операция над каждым элементом int fifo_for_each (struct fifo * fifo, void (*func)(struct fifo_item*));
#endif Начало и завершение работы со структурой очереди будет производится соответственно fifo_init и fifo_exit. Второй аргумент fifo_init — это функция, которая будет вызываться при завершении работы с очередью. Данная функция служит для освобождения памяти, занимаемой элементом буфера, при завершении работы с буфером.Добавление и извлечение данных производится при помощи fifo_push и fifo_pop соответственно. Данные в очереди хранятся по указателю, который передается вторым аргументом fifo_push, и возвращается fifo_pop.
Для выполнения однотипных операций над элементами очереди служит fifo_for_each. Вторым аргументом передается функция, реализующая требуемую операцию. Ниже приведена возможная реализация.
Файл fifo.с:
#include <stdlib.h>
#include «fifo.h»
int fifo_init (struct fifo *fifo, void (*item_free)(struct fifo_item*)) { INIT_LIST_HEAD (&(fifo→headlist));
fifo→length = 0; fifo→item_free=item_free;
return 0; }
int fifo_exit (struct fifo* fifo) { int res=0; struct list_head *pos, *q; struct fifo_item* item;
list_for_each_safe (pos, q, &(fifo→headlist)) { item=list_entry (pos, struct fifo_item, list); fifo→item_free (item); list_del (pos); free (item); }
return res; }
int fifo_push (struct fifo* fifo, void* data) { int res=-1; struct fifo_item* item;
item=(struct fifo_item*)malloc (sizeof (struct fifo_item));
if (NULL!= item) { item→data = data; list_add_tail (&(item→list), &(fifo→headlist));
fifo→length++; }
return res; }
void* fifo_pop (struct fifo* fifo) { void* res=NULL; struct fifo_item* item=list_entry (fifo→headlist.next, struct fifo_item, list);
if (! list_empty (&(fifo→headlist))) { res = item→data;
list_del (&(item→list));
free (item);
fifo→length--; }
return res; }
int fifo_for_each (struct fifo* fifo, void (*func)(struct fifo_item*)) { int res = 0; struct fifo_item* item;
list_for_each_entry (item, &(fifo→headlist), list) func (item);
return res; } В fifo_init инициализируется «голова» списка: INIT_LIST_HEAD (&(fifo→headlist)), устанавливается нулевая длина и метод для очистки памяти при завершении работы с очередью.В fifo_exit производится «защищенный» проход по всем элементам списка. Для каждого элемента очереди производится освобождение памяти, выделенной под данные, после чего элемент удаляется из списка, а память, которую он занимал, освобождается.
В fifo_push выделяется память под элемент списка. Если эта операция произведена успешно, в элементе очереди сохраняется указатель на данные и элемент добавляется в хвост списка. Длина очереди, соответственно, увеличивается.
В fifo_pop сначала находится первый элемент очереди. Если список не пуст и такой элемент найден, сохраняется указатель на данные. Затем элемент удаляется из списка, а память, которую он использовал, освобождается. Длина очереди, соответственно, уменьшается.
Чтобы использовать эту реализацию очереди для некоторой произвольной структуры данных, необходимо создать метод free для освобождения памяти данных элемента при завершении работы с буфером, а также метод для типовой операции над элементами буфера, если он требуется.
В данном примере используются mydata_free, который служит для освобождения памяти, выделенной под данные элемента очереди, а также mydata_print — функция, которая выводит элементы очереди на экран. В качестве данных выступает тип float в структуре mydata.
Файл main.с:
#include <stdlib.h>
#include «fifo.h»
struct mydata { float value; };
void* mydata_create (void) { return (struct mydata *)malloc (sizeof (struct mydata)); }
void mydata_free (struct fifo_item* item) { free (item→data); }
void mydata_print (struct fifo_item* item) { struct mydata* data = (struct mydata*)item→data;
printf («item: %f\n», data→value); }
int main () { int i; struct fifo myfifo; struct mydata* newdata;
// начало работы с FIFO fifo_init (&myfifo, mydata_free);
for (i=0; i<10; i++) { // новые данные newdata=mydata_create();
if (! newdata) { return -1; }
newdata→value = (float)i*0.1;
// добавляем в FIFO fifo_push (&myfifo, newdata); }
// печать данных printf («FIFO:\n»); fifo_for_each (&myfifo, mydata_print); printf (»\n»);
do { newdata = fifo_pop (&myfifo);
if (newdata) { printf («pop: %f\n», newdata→value); }
if (myfifo.length == 5) { printf (»\nFIFO:\n»); fifo_for_each (&myfifo, mydata_print); printf (»\n»); }
} while (newdata);
// конец работы с FIFO fifo_exit (&myfifo);
return 0; } Функция main содержит тестовые операции с буфером.
Результат работы:
$ ./testfifo FIFO: item: 0.000000 item: 0.100000 item: 0.200000 item: 0.300000 item: 0.400000 item: 0.500000 item: 0.600000 item: 0.700000 item: 0.800000 item: 0.900000
pop: 0.000000 pop: 0.100000 pop: 0.200000 pop: 0.300000 pop: 0.400000
FIFO: item: 0.500000 item: 0.600000 item: 0.700000 item: 0.800000 item: 0.900000
pop: 0.500000 pop: 0.600000 pop: 0.700000 pop: 0.800000 pop: 0.900000 Используемые источники 1.Linux Kernel Linked List Explained (русский перевод)