Następna Poprzednia Spis

3. Kolejki procesów oczekujących

3.1 Opis

Procesy o statusie TASK_INTERRUPTIBLE i TASK_UNINTERRUPTIBLE są podzielone na wiele klas, z których każda odpowiada zdarzeniu na jakie oczekują i jest reprezentowana przez kolejkę procesów oczekujących (ang. wait_queue).

W pliku wait.h jest zdefiniowana struktura i operacje na kolejkach procesów czekających. Jest tam dużo opcji używanych podczas testowania poprawności, jednak przy normalnym funkcjonowaniu systemu nie są one wykorzystywane, nie będziemy więc o tym wspominać.

3.2 Struktura

Element kolejki wait_queue jest postaci:


struct wait_queue_t 	{
	unsigned int flags;
	struct task_struct * task;
	struct list_head task_list;
}

Jedynymi wykorzystywanymi wartościami pola flagsWQ_FLAG_EXCLUSIVE i jej zaprzeczenie. Gdy zajdzie określone zdarzenie, przeglądana jest odpowiednia kolejka wait_queue i budzone są kolejne procesy, aż do napotkania określonej ilości elementów z flagą WQ_FLAG_EXCLUSIVE. Wtedy kończy się seria procesów budzonych. Oznacza to, że jeśli wszystkie elementy w kolejce mają ustawioną flagę WQ_FLAG_EXCLUSIVE, to procesy mogą być budzone pojedynczo.

Widać, że również ta kolejka jest oparta na list.h . Tym razem potrzebujemy wskaźnika do struktury procesu, bo istnieje dowolnie wiele różnych kolejek, więc dowiązanie do kolejki oczekujących nie może być elementem struktury opisującej proces.

Początek listy jest elementem wyróżnionym o trochę innej strukturze:


struct wait_queue_head_t{
	wq_lock_t lock;
	struct list_head task_list;
}
 	

Początek wait_queue nie jest związany z żadnym procesem, za to posiada pole używane do zapewnienia niepodzielności operacji na jego kolejce. Kolejki procesów oczekujących są obsługiwane zarówno przez funkcje jądra jak i przez przerwania, więc operacje na nich (wstawianie i usuwanie) muszą być wykonywane z wyłączonymi przerwaniami.

3.3 Operacje na kolejkach procesów oczekujących

Do tworzenia nowych kolejek używane są makra: Można tez skorzystać z funkcji inicjujących.

Sprawdzenie, czy kolejka wait_queue jest używana polega na sprawdzeniu, czy związana z nią lista nie jest pusta.

int waitqueue_active(wait_queue_head_t *kolejka)

Dostępne są funkcje umożliwiające dokonywanie prostych operacji na kolejkach:


void __add_wait_queue(wait_queue_head_t *poczatek, 
			wait_queue_t *nowy_element)

void __add_wait_queue_tail(wait_queue_head_t *poczatek,
			wait_queue_t *nowy_element)

void __remove_wait_queue(wait_queue_head_t *poczatek,
			wait_queue_t *usuwany_element)

 

Są one zaimplementowane przy użyciu standardowych funkcji z list.h Funkcje te nie zapewniają jednak atomowości, dlatego bardziej użyteczne są funkcje wykorzystujące przewidziane dla kolejki zabezpieczenia i ustawiające odpowiednio flagi:


void add_wait_queue(wait_queue_head_t *kolejka,
			wait_queue_t *nowy_element)
			
void add_wait_queue_exclusive(wait_queue_head_t *kolejka, 
			wait_queue_t * nowy_element)
			
void remove_wait_queue(wait_queue_head_t *kolejka, 
			wait_queue_t * element)

Te funkcje już zapewniają atomowość operacji.

3.4 Różnice w stosunku do poprzedniej wersji

Na koniec omówmy jeszcze różnice między obecną strukturą kolejki, a tą z poprzedniej wersji jądra:


Następna Poprzednia Spis