| Okładka... | >>> |
|---|
Autor:
Tomasz Stachowicz
ts181294@zodiac.mimuw.edu.pl
2001.12.19
Spis treści:
| >>> |
| Wprowadzenie... | >>> |
|---|
Autor: Jeff Bonwick (Sun Microsystems)
The Slab Allocator: An Object-Caching Kernel Memory Allocator
Obiekt = obszar danych o ustalonym rozmiarze
Problem: częste przydzielanie i zwalnianie obiektów standardowym algorytmem było kosztowne
Rozwiązanie: zamiast niszczenia obiektu - wstawianie go do cache'a, zamiast tworzenia - pobieranie z cache'a
| Zarys ogólny... | >>> |
|---|
Przydzielanie i zwalnianie obiektów następuje poprzez cache'e.
Każdy cache ma ustalony rozmiar obiektów i typ pamięci.
Dostępne operacje:
|
|
| Zarys ogólny cd... | >>> |
|---|
Każdy cache zawiera listę tafli (ang. slab).
Tafla to ciągły blok pamięci obejmujący jedną lub kilka ramek, podzielony na obiekty tego samego rozmiaru.
W obrębie jednego cache'a tafle są posortowane według następującej reguły:
najpierw tafle pełne, potem niepełne, na końcu puste.
Organizacja wewnętrzna tafli zależy od rozmiaru obiektów:
PAGE_SIZE/8, deskryptory (struktury opisujące)
tafli i obiektów znajdują się w tym samym bloku pamięci, co same obiekty,
| Ilustracja... | >>> |
|---|
| Struktury... | >>> |
|---|
typedef unsigned int kmem_bufctl_t;
typedef struct slab_s {
struct list_head list;
unsigned long colouroff;
void *s_mem; wskaźnik do pierwszego obiektu
unsigned int inuse; liczba aktualnie używanych obiektów
kmem_bufctl_t free; indeks pierwszego wolnego obiektu
kmem_bufctl_t nextfree[num];
jeśli i-ty obiekt jest używany, to nextfree[i]
zawiera indeks następnego wolnego obiektu
lub BUFCTL_END, gdy jest ostatnim wolnym
} slab_t;
Uwaga! W rzeczywistości w strukturze nie występuje zmienna nextfree
(gdyż nie ma z góry ustalonego rozmiaru). Jednak w źródłach Linuksa struktury
slab_s używa się tak, jakby ta zmienna w niej występowała i miała rozmiar
num ze struktury kmem_cache_s, czyli liczba obiektów w tafli.
| Struktury cd... | >>> |
|---|
struct kmem_cache_s {
struct list_head next; następny deskryptor cache'a
struct list_head slabs; lista tafli w danym cache'u
struct list_head *firstnotfull; pierwsza niepełna tafla
unsigned int objsize; rozmiar obiektu
unsigned int num; liczba obiektów w tafli
unsigned int gfporder; 2^gfporder == liczba ramek na taflę
kmem_cache_t *slabp_cache; cache cache'a
void (*ctor)(...); konstruktor obiektu
void (*dtor)(...); destruktor obiektu
};
#define MAX_OBJ_ORDER 5 2^MAX_OBJ_ORDER == maksymalna liczba
ramek na obiekt
| Cache'e ogólnego przeznaczenia... | >>> |
|---|
typedef struct cache_sizes {
size_t cs_size; rozmiar cache'a
kmem_cache_t *cs_cachep; wskaźnik na cache o tym rozmiarze
dla zwykłej pamięci
kmem_cache_t *cs_dmacachep; dla pamięci DMA
} cache_sizes_t;
static cache_sizes_t cache_sizes[] = {
{ 32, NULL, NULL},
{ 64, NULL, NULL},
.....
{ 65536, NULL, NULL},
{131072, NULL, NULL},
{ 0, NULL, NULL}
};
Tablica cache_sizes jest inicjowana przy starcie systemu zestawem
cache'y ogólnego przeznaczenia.
| Algorytm kmalloc... | >>> |
|---|
kmalloc(size_t size, int flags)
w tablicy cache_sizes znajdź pierwszy pasującą pozycję lub zwróć błąd
z tej pozycji wybierz odpowiedni cache \
- w zależności od tego, czy flaga GFP_DMA jest w flags
wywołaj kmem_cache_alloc dla tego cache'a i zwróć wynik
kmem_cache_alloc(kmem_cache_t *cachep, int flags)
powtarzaj
zablokuj przerwania
jeśli jest niepełna tafla, to
weź adres pierwszego wolnego obiektu w tej tafli
usuń go z listy wolnych
odblokuj przerwania
zwróć adres obiektu
wpp
odblokuj przerwania
spróbuj dodać nową taflę do cache'a
jeśli się nie udało, to
zwróć błąd
| Algorytm kfree... | >>> |
|---|
kfree (const void *objp)
zablokuj przerwania
znajdź cache, do którego należy obiekt
wywołaj kmem_cache_free dla tego cache'a i obiektu
odblokuj przerwania
kmem_cache_free(kmem_cache_t *cachep, void *objp)
znajdź taflę zawierającą dany obiekt
wstaw obiekt do listy wolnych w tej tafli
przesuń tą taflę na liście tak, by zachowany był porządek: \
najpierw tafle pełne, później niepełne, na końcu puste
| Różnice między 2.2 a 2.4... | >>> |
|---|
| 2.2 | 2.4 |
| dma i normal - różne tafle, ale w obrębie tego samego cache'a | dla każdego rodzaju pamięci (normal, dma, highmem) osobny cache |
| deskryptor tafli na końcu tafli | na początku |
| brak optymalizacji dla SMP | optymalizacje dla SMP |
| nie można usunąć cache'a z pamięci | można |
| >>> |
| Wprowadzenie... | >>> |
|---|
2 rodzaje organizacji:
Każde urządzenie wymiany składa się z sekwencji slotów stron (ang. page slots), czyli bloków do przechowywania wymienianych stron.
swap_info - tablica z informacjami o urządzeniach wymiany;
max rozmiar określony przez stałą MAX_SWAPFILES
(zwykle 8);
Informacje o urządzeniu opisane są stukturą swap_info_struct.
| Struktury... | >>> |
|---|
struct swap_info_struct
{
unsigned int flags; czy używane i czy można zapisywać
kdev_t swap_device; numer urządzenia
struct dentry * swap_file; i-węzeł urządzenia
unsigned short * swap_map; wskaźnik do tablicy liczników
odwołań do slotów stron
unsigned char * swap_lockmap; wskaźnik do tablicy blokad
bitowych slotów stron
unsigned int lowest_bit; od tej pozycji należy zacząć
szukanie wolnego slotu
unsigned int highest_bit; na tej pozycji należy zakończyć
unsigned int cluster_next; następny slot do sprawdzenia
przy szukaniu wolnego
unsigned int cluster_nr; liczba używanych slotów
int prio; priorytet urządzenia
int pages; ile wolnych slotów na urządzeniu
unsigned long max; rozmiar urządzenia wymiany w stronach
int next; następne urządzenie
na liście priorytetowej
}
| Ilustracja... | >>> |
|---|
| Zmienne globalne... | >>> |
|---|
nr_swapfiles - maksymalny indeks urządzenia wymiany w tablicy swap_info
nr_swap_pages - całkowita liczba wolnych slotów we wszystkich urządzeniach wymiany
Informacje o urządzeniach wymiany znajdują się na liście uporządkowanej wg priorytetów.
Indeks pierwszego elementu tej listy trzymany jest w zmiennej swap_list typu:
struct swap_list_t {
int head; głowa listy
int next; następne urządzenie do rozpatrzenia
(patrz: Wybór urządzenia do zrzucenia strony)
}
| Dodawanie urządzenia wymiany... | >>> |
|---|
int sys_swapon(const char *specialfile, int swap_flags)
specialfile - ścieżka do pliku urządzenia (partycji) lub zwykłego pliku
swap_flags - jeśli ustawiona flaga SWAP_FLAG_PREFER,
to najmłodsze 15 bitów określa priorytet urządzenia
Funkcja znajduje pierwszy wolne miejsce w tablicy swap_info, wstawia tam
dane urządzenie, inicjuje tablicę liczników i tablicę blokad, zwiększa
liczbę wolnych slotów (nr_swap_pages), wstawia urządzenie do listy priorytetowej.
Jeśli w swap_flags bit SWAP_FLAG_PREFER jest zgaszony,
to priorytet urządzenia nadawany jest zgodnie z licznikiem least_priority,
zmniejszanym po każdym wykorzystaniu.
| Usuwanie urządzenia wymiany... | >>> |
|---|
int sys_swapoff(const char *specialfile)
Funkcja usuwa urządzenie z listy i ustawia mu flagę, że nie można zapisywać.
Przy pomocy try_to_unuse() próbuje zwolnić wszystkie strony
zrzucone na to urządzenie. Jeżeli się uda, zwalnia odpowiednie pole
w tablicy swap_info i ustawia flagę, że urządzenie jest nieużywane.
Wpp. wstawia urządzenie z powrotem do listy i wychodzi z błędem.
| Wybór urządzenia wymiany... | >>> |
|---|
Wybór urządzenia odbywa się w funkcji
unsigned long get_swap_page(void)
służącej do przydzielenia wolnego slotu strony.
W celu znalezienia urządzenia, funkcja wykonuje dwa przejścia po liście urządzeń.
Za pierwszym razem zaczyna od swap_list.next i idzie tak długo,
aż zmieni się priorytet lub skończy lista. Następnie rozpoczyna od początku listy
(swap_list.head) i idzie aż do końca.
Jeśli w ciągu obu przejść nie znajdzie urządzenia, to znaczy że nie ma wolnego miejsca
i wychodzi z błędem. Wpp. zwraca zakodowany numer urządzenia i numer slotu.
Pole swap_list.next ustawiane jest na następnik tego urządzenia,
chyba że było ono ostatnim o danym priorytecie, wówczas
swap_list.next = swap_list.head.
| That's all folks... |
|---|