Przydzial pamieci dla jadra.


Zgodnie z poczatkowymi zalozeniami UNIX'a dynamiczny przydzial pamieci dla jadra nie byl przewidziany. Jadra posiadalo zarezerwowane tablice o stalych rozmiarach, ktorych polami gospodarowalo w zaleznosci od potrzeb. Linux, ktory bazuje na UNIX'ie, posiada organizacje mieszana. Pozostalo wiekszosc tablic np. tablica procesow, jednoczesnie czesc struktur jest alokowana dynamicznie, korzystajac z wolnej pamieci dostepnej dla systemu.


Procedury obslugujace przydzial pamieci dla jadra znajduja sie w pliku 'linux/mm/kmalloc.c'. Sa to:

void *kmalloc( size_t size,    /* rozmiar szukanej pamieci */ 
               int priority    /* rodzaj pamieci (DMA lub dowolna) */
 
void kfree( void *__ptr        /* adres zwalnianego obszaru */ ) 

Jest to specyficzny rodzaj przydzialu pamieci, pamiec zorganizowana za pomoca stron o ustalonym rozmiarze jest dodatkowo dzielona na bloki o wielkosciac odpowiadajacych potedze dwojki, tak ze kazda strona zawiera jednorozmiarowe bloki. Zadanie przydzialu jest zaokraglane do minimalnego bloku, w ktorym sie miesci. Maksymalnie jadro moze jednorazowo zarzadac przydzielenia dla siebie 5 (pieciu) stron o rozmiarach zazwyczaj 4 lub 8 kB.

Kazdy obszar (jedna lub wiecej stron, gdy blok nie miesci sie w stronie) dolaczany do jadra posiada pole (struct page_descriptor) identyfikujace go, znajdujace sie zawsze na poczatku takiego obszaru. Pole to zajmuje 16 bajtow.

Podobnie kazdy blok posiada pole identyfikacyjne (struct block_header), rowniez znajdujace sie na jego poczatku i zajmujace 8 bajtow. Dzieki takiej organizacji jadro majac adres poczatku wolnego bufora pamieci moze szybko dotrzec do jego pol identyfikacyjnych.

struct page_descriptor { 
        struct page_descriptor *next; 
        struct block_header *firstfree;       /* pierwszy wolny blok */ 
        int order;                            /* liczba wymaganych stron na blok */ 
        int nfree;                            /* liczba wolnych blokow w stronie */ 
}; 
struct block_header {
        unsigned long bh_flags;               /* flagi okreslajace m.in. czy zajety */ 
        union {
               unsigned long ubh_length;      /* dlugosc bloku, gdy zajety */ 
               struct block_header *fbh_next; /* nastepny blok, gdy wolny */ 
        } vp;
}; 

Do przechowywania danych o wolnych buforach sluzy struktura size_descriptor.

struct size_descriptor {
        struct page_descriptor *firstfree; 
        /* strony zawierajace bloki dostepne dla jadra */ 
        struct page_descriptor *dmafree;   
        /* j.w. tylko pamiec z dostepem DMA np. uzywana przez urzadzenia zewnetrzne */ 
        int nblocks;
        /* ilosc blokow w stronie */ 
        int nmallocs; 
        int nfrees;                        
        /* ilosc wolnych blokow */ 
        int nbytesmalloced; 
        int npages; 
        unsigned long gfporder; 
        /* liczba stron potrzebnych do stworzenia jednego bloku (gdy blok jest wiekszy
        od strony) */ 
}; 

Kazdemu rozmiarowi bloku odpowiada pole (w tablicy sizes[ ]) zawierajace liste stron dostepnych dla blokow o danej wielkosci

struct size_descriptor sizes[ ] 

Kazda pozycja w p.w. tablicy oznacza bloki o rozmiarach kolejno (rozmiar wraz z deskryptorem) 32, 64, 128, 252(!), 508(!), itd.

Schemat organizacji pamieci32.


Algorytm kmalloc(size,priority);

1. znajdz odpowiednia pozycje w tablicy sizes[ ] (tj. minimalny rozmiar bloku wystarczajacy na przydzial pamieci rozmiaru size).

2. jesli size zbyt duzy zakoncz bledem.

3. jesli brak strony na liscie stron odpowiedniego pola idz 7 (no_bucket_page)

4. jesli brak wolnego bloku na stronie idz 11 (not_free_on_freelist)

5. zmniejsz liczbe blokow na stronie, ewentualnie przejdz do nastepnej strony na liscie (gdy brak wolnych blokow), zamarkuj blok.

6. zwroc adres wolnej pamieci.

7. (no_bucket_page) zaalokuj jedna lub odpowiednio wiecej stron z przestrzeni dostepnej dotychczas dla programow uzytkownika.

8. jesli brak wolnych stron w pamieci idz 10 (no_free_page)

9. dopisz strone z jej wolnymi blokami do odpowiedniej pozycji w tablicy sizes[ ], idz 5.

10. (no_free_page) przeszukaj bufor zwalnianych przez jadro stron w poszukiwaniu jakiejkolwiek (uwaga1. strona zwalniana przez jadro nie musi byc odrazu oddana do przestrzeni uzytkownika, ale moze byc najpierw przekazywana do tablicy kmalloc_cache[MAX_CACHE_ORDER=3]. Dopiero po zapelnieniu pamieci podrecznej kolejne strony sa oddawane dla uzytkownika), jezeli znaleziono odpowiednia liczbe stron idz 9, wpp. wyjdz z bledem.

11. (not_free_on_freelist) zglos blad w tablicy sizes[ ] (niewytlumaczalny), wyjdz.


Algorytm kfree(_ptr)

1. jesli _ptr==NULL wyjdz z bledem.

2. sprawdz czy istnieja pola identyfikacyjne bloku i strony dla tego obszaru pamieci oraz czy sa sensowne, jesli nie - wyjdz z bledem.

3. dopisz wolny blok do odpowiadajecej mu strony (listy wolnych blokow danej strony).

4. znajdz odpowiadajaca mu pozycje w tablicy sizes[ ].

5. jezeli na stronie nie bylo wczesniej wolnych blokow oraz nie jest jeszcze zupelnie pusta dopisz ja do listy wolnych stron (sizes[ ]->firstfree/dmafree), wyjdz z procedury.

6. jezeli strona jest zupelnie pusta zwolnij ja (patrz uwaga1).

7. wyjdz.


autor: Grzegorz Zaprzalek