Następna Poprzednia Spis

1. Struktura list.h

1.1 Wprowadzenie

Naszą opowieść o kolejkach procesów w Linuksie warto rozpocząć od omówienia pliku list.h. Zawiera on definicję kolejki oraz implementację podstawowych operacji na niej. Struktura kolejki jest bardzo ogólna i umożliwia używanie jej w bardzo wielu sytuacjach. Praktycznsie wszędzie, gdzie w Linuksie 2.4.7 potrzebne jest szeregowanie czy grupowanie obiektów wykorzystywane są właśnie te kolejki, potocznie zwane strukturą list.h.

1.2 Struktura

Kolejki są implementowane jako dwukierunkowe listy cykliczne. Węzeł na takiej liście zawiera wskaźniki do następnego i poprzedniego elementu listy:

struct list_head {
	struct list_head *next, *prev;
};

Za pustą uznaje się listę składającą się z jednego elementu (atrapy, wskaźnika na listę), dla którego następnik (a także poprzednik) wskazują na niego samego.

1.3 Operacje na kolejkach

Mamy do dyspozycji makra (których można używać w różnych miejscach kodu) inicjujące pustą kolejkę - to znaczy taką, w której obydwa wskaźniki wskazują jej jedyny element.

Funkcje wewnętrzne zaczynają się od dwóch podkreśleń i zakładają rzeczywistą znajomość wskaźników do następnego i poprzedniego elementu w stosunku do tego, którym manipulujemy.

void __list_add(struct list_head *nowy, poprzedni, następny)
void __list_del(struct list_head * poprzedni, następny)

Powyższe funkcje wykonują oczywiste manipulacje na wskaźnikach. Są to praktycznie jedyne operacje na kolejkach, które jawnie zmieniają wskaźniki ze struktury list_head. Oczywiście są od tej zasady wyjątki, o których wspomnimy później.

Przy pomocy funkcji wewnętrznych definiowane są podstawowe operacje na kolejce dostępne dla innych części jądra:

1.4 Makro list_entry

Skoro jednak istnieje osobna struktura kolejki, to czy możemy dostać się do samego obiektu, który stoi w kolejce? Przecież "wędrując" po kolejce napotykamy tylko elementy typu head_list, które same w sobie nic nie znaczą...

Jednak kluczowym jest spostrzeżenie, że skoro list_head jest polem w jakiejś strukturze, to możemy łatwo wyśledzić, gdzie w pamięci mieści się owa struktura, jeśli tylko mamy o niej parę informacji. Język C umożliwia takie manipulacje, dlatego też dostępne jest makro (jego treść jest oparta na licznych rzutowaniach):

list_entry(wskaznik_na_liste, typ, pole)

Zwraca ono obiekt typu typ, którego pole o podanej nazwie jest typu list_head i zawiera element listy wskazywany przez wskaźnik_na_listę.

Przykład użycia:

struct list_head *jakas_lista;
proces = list_entry(jakas_lista, struct task_struct, run_list);

1.5 Różnice w stosunku do poprzedniej wersji

Należy podkreślić, że obecna struktura kolejki różni się od stosowanej we wcześniejszych wersjach Linuksa.

Wcześniej, jeśli jakiś obiekt wymagał szeregowania, to jego struktura zawierała wskaźniki na poprzedni i następny obiekt tego samego typu. Operacje na liście wymagały szeregu zmian wartości wskaźników. Treści procedur np. dodających do listy powtarzały się, ale ich ujednolicenie było trudne ze względu na odmienną budowę struktur różnych obiektów.

Teraz taki obiekt ma pole typu list_head i przy użyciu prostych procedur z list.h może dokonywać operacji na listach. Skraca to kod i czyni go mniej podatnym na błędy, jednak najważniejsze jest chyba, że lista jest całkiem niezależna od obiektów które zawiera.

Taka implementacja kolejek jest bardzo szeroko używana, między innymi:

Jeszcze raz podkreślimy różnice w stosunku do poprzedniej wersji:


Następna Poprzednia Spis