Приветствую!
В данной статье рассматривается использование реализации двусвязного списка ядра Linux.
Двусвязный список в ядре Linux реализован в файле include/linux/list.h. Мы будем использовать адаптированный вариант list.h [1], который отличается от оригинального возможностью использовать его в userspace. Например, создадим очередь — структуру данных с доступом к элементам по принципу «первый пришёл — первый вышел» для произвольного типа данных на основе list.h.
Для этого создаем структуру очереди и структуру элемента очереди:
Файл fifo.h:
Начало и завершение работы со структурой очереди будет производится соответственно fifo_init и fifo_exit. Второй аргумент fifo_init — это функция, которая будет вызываться при завершении работы с очередью. Данная функция служит для освобождения памяти, занимаемой элементом буфера, при завершении работы с буфером.
Добавление и извлечение данных производится при помощи fifo_push и fifo_pop соответственно. Данные в очереди хранятся по указателю, который передается вторым аргументом fifo_push, и возвращается fifo_pop.
Для выполнения однотипных операций над элементами очереди служит fifo_for_each. Вторым аргументом передается функция, реализующая требуемую операцию. Ниже приведена возможная реализация.
Файл fifo.с:
В fifo_init инициализируется «голова» списка: INIT_LIST_HEAD(&(fifo->headlist)), устанавливается нулевая длина и метод для очистки памяти при завершении работы с очередью.
В fifo_exit производится «защищенный» проход по всем элементам списка. Для каждого элемента очереди производится освобождение памяти, выделенной под данные, после чего элемент удаляется из списка, а память, которую он занимал, освобождается.
В fifo_push выделяется память под элемент списка. Если эта операция произведена успешно, в элементе очереди сохраняется указатель на данные и элемент добавляется в хвост списка. Длина очереди, соответственно, увеличивается.
В fifo_pop сначала находится первый элемент очереди. Если список не пуст и такой элемент найден, сохраняется указатель на данные. Затем элемент удаляется из списка, а память, которую он использовал, освобождается. Длина очереди, соответственно, уменьшается.
Чтобы использовать эту реализацию очереди для некоторой произвольной структуры данных, необходимо создать метод free для освобождения памяти данных элемента при завершении работы с буфером, а также метод для типовой операции над элементами буфера, если он требуется.
В данном примере используются mydata_free, который служит для освобождения памяти, выделенной под данные элемента очереди, а также mydata_print — функция, которая выводит элементы очереди на экран. В качестве данных выступает тип float в структуре mydata.
Файл main.с:
Функция main содержит тестовые операции с буфером.
Результат работы:
1.Linux Kernel Linked List Explained (русский перевод)
В данной статье рассматривается использование реализации двусвязного списка ядра Linux.
Двусвязный список в ядре Linux реализован в файле include/linux/list.h. Мы будем использовать адаптированный вариант list.h [1], который отличается от оригинального возможностью использовать его в userspace. Например, создадим очередь — структуру данных с доступом к элементам по принципу «первый пришёл — первый вышел» для произвольного типа данных на основе 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 (русский перевод)
