Omówię tu sposób, w jaki Linux wykorzystuje pliki i urządzenia wymiany w celu wirtualnego rozszerzenia dostępnej w systemie pamięci RAM.
-
Urządzenie wymiany
-
urządzenie, najczęściej dyskowe, umożliwiające zachowywanie na nim stron pamięci. Może być zaimplementowane jako:
- plik stałej wielkości - jego części mogą być zapisane w różnych miejscach dysku, co spowalnia czas dostępu
- urządzenie blokowe - oddzielna partycja dysku twardego
-
Obszar wymiany
-
miejsce udostępniane przez urządzenie/plik wymiany do zapisywania stron pamięci.
-
Slot strony
-
blok obszaru wymiany zawierający dokładnie jedną stronę (Linux: 4096 B). Każdy obszar wymiany dzielimy na szereg kolejnych slotów.
Linux umożliwia zdefiniowanie MAX_SWAPFILES
(obecnie 8) obszarów wymiany.
Posiadanie kilku obszarów wymiany umożliwia:
dynamiczne zwiększanie przestrzeni wymiany (bez restartu systemu)
umieszczenie obszarów wymiany na różnych dyskach - można z nich korzystać jednocześnie
Pierwszy slot obszaru wymiany zawiera informacje o obszarze wymiany - jego format opisuje unia swap_header
.
Na globalnej zmiennej nr_swap_pages
przechowywana jest ilość wolnych slotów we wszystkich obszarach wymiany.
W tablicy swap_info
przechowywane są deskryptory obszarów wymiany, zdefiniowane następująco:
struct swap_info_struct {
unsigned int flags; // SWP_USED - czy obszar wymiany jest aktywny
SWP_WRITEOK -czy do obszaru można zapisywać
kdev_t swap_device; // numer urządzenia
spinlock_t sdev_lock;
struct dentry * swap_file; // pozycja katalogu pliku/pliku urządzenia
struct vfsmount *swap_vfsmnt;
unsigned short * swap_map; // tablica liczników użycia slotów
unsigned int lowest_bit; // pierwszy wolny slot
unsigned int highest_bit; // ostatni wolny slot
int prio; // priorytet urządzenia
int pages; // liczba użytecznych slotów
unsigned long max; // rozmiar w stronach
unsigned int cluster_next; //
unsigned int cluster_nr; // wykorzystywane przy wyborze wolnego slotu
int next; //
};
Aby stwierdzić, gdzie dana strona została wymieniona, należy zidentyfikować obszar wymiany i numer slotu, w którym się znajduje. Informacje te tworzą identyfikator wymienionej strony, zapisywany w momencie wymiany w odpowiedniej pozycji Tablicy Stron.
Najmłodszy bit identyfikatora - flaga Present - jest wyzerowany, co oznacza, że strona opisywana przez daną pozycję Tablicy Stron nie znajduje się w pamięci RAM.
W manipulowaniu identyfikatorem wymienionej strony pomagają następujące makra:
SWP_TYPE(id_strony) - pobierz numer obszaru
SWP_OFFSET(id_strony) - pobierz indeks slotu strony
SWP_ENTRY(type, offset) - stwórz identyfikator wymienionej strony
Aby móc z urządzenia wymiany korzystać, superużytkownik musi je uaktywnić.
Służy do tego funkcja:
long sys_swapon(const char * specialfile, /* ścieżka dostępu do pliku urządzenia/zwykłego
pliku, który chcemy aktywować */
int swap_flags) /* priorytet urządzenia (domyślnie -1) */
Funkcja ta:
sprawdza poprawność danych o urządzeniu zawartych w 1-szym slocie, wpisanych tam w momencie tworzenia partycji wymiany
znajduje wolne miejsce w tablicy swap_info i umieszcza tam nowy deskryptor obszaru wymiany
inicjuje pola deskryptora (ustawia liczbę uszkodzonych stron, alokuje pamięć na tablice, itp)
Proces odłączania urządzenia jest o wiele bardziej skomplikowany. Wykonuje go funkcja:
long sys_swapoff(const char * specialfile) /* ścieżka dostępu do pliku urządzenia lub
zwykłego pliku, który chcemy aktywować */
Problemem jest wczytanie wszystkich stron z odłączanego urządzenia do pamięci RAM oraz uaktualnienie we wszystkich tablicach stron wszystkich procesów identyfikatora wymienionej strony na adres fizyczny wczytanego bloku stronicowego.
Jeśli w pewnym momencie zabraknie pamięci na wczytanie kolejnej strony z odłączanego urządzenia, operacja kończy się niepowodzeniem i urządzenie wymiany pozostaje aktywne.
Główny priorytet: zajęte sloty muszą tworzyć jak najbardziej spójny obszar
Powód: zminimalizowanie czasu przeszukiwania dysku przy korzystaniu z obszaru wymianu
Przyjęte rozwiązanie:
Staramy się, by wymieniane strony tworzyły w obszarze wymiany jak najbardziej spójne bloki. W tym celu dla stron wchodzących w skład jednej grupy alokuje się kolejne wolne sloty. Grupę taką nazywamy klastrem - jego rozmiar wynosi SWAPFILE_CLUSTER (tu: 256). Gdy zaalokujemy SWAPFILE_CLUSTER stron, zaczynamy nowy klaster. Będzie to pierwszy poczynając od początku obszaru wymiany spójny blok SWAPFILE_CLUSTER slotów. Może się jednak zdażyć, iż takiego bloku nie ma - wtedy zaczynamy 'wypełniać dziury' - alokujemy wolne sloty poczynając od pierwszego wolnego, dopóki nie wypełnimy SWAPFILE_CLUSTER slotów.
Zadanie to wypełnia funkcja scan_swap_map()
, która znajduje wolny slot w danym obszarze wymiany.
int scan_swap_map
(struct swap_info_struct *si, /* wsk. na deskr. obszaru wymiany */
unsigned short count) /* liczba użytkowników slotu */
/* i-ty slot jest wolny <=> si->swap_map[i] == 0
cluster_next - indeks slotu strony, od którego zaczynamy szukać wolnego slotu
cluster_nr - liczba dotychczas zaalokowanych slotów w klastrze
*/
jeśli w obszarze wymiany nie ma już wolnych slotów rób
return 0
jeśli si->cluster_nr > 0 rób
offset = min{ nr wolnego slotu o numerze >= si->cluster_next }
si->cluster_nr --
wpp { /* rozpocznij nowy klaster */
si->cluster_nr = SWAPFILE_CLUSTER;
jeśli istnieje spójny ciągu SWAPFILE_CLUSTER wolnych slotów rób
offset = min { numer pierwszego slotu takiego obszaru }
wpp
offset = pierwszy wolny slot
}
Uaktualnij pola si->lowest_bit i si->highest_bit
si->swap_map[offset] = count;
nr_swap_pages--;/* liczba wolnych slotów zmniejsza się */
si->cluster_next = offset+1;/* w tym miejscu zaczniemy szukać następnym razem */
return offset; /* zwróć indeks zaalokowanej strony */
Za wybór slotu, w którym zostanie zapisana wymieniana na dysk strona odpowiada funkcja
swp_entry_t __get_swap_page(unsigned short count)
Na początku ustala ona, z którego obszaru wymiany skorzystać. Kładzie się nacisk na wykorzystanie obszarów położonych na najszybszych dyskach. Każdy deskryptor obszaru wymiany w polu prio przechowuje informację o priorytecie obszaru - im szybszy dysk tym wyższy priorytet. Deskryptory aktywnych obszarów wymiany tworzą posortowaną malejąco względem pola prio listę, na której początek wskazuje zmienna swap_list. Gdy slot danego obszaru wymiany o priorytecie równym X zostanie zajęty, to następnym razem wolnego slotu będziemy poszukiwać poczynając od:
- następnego obszaru wymiany na liście swap_list, jeśli jego priorytet jest równy X
- pierwszego obszaru wymiany na liście swap_list w przeciwnym przypadku.
Podejście to zapewnia równomierne obciążenie obszarów wymiany o tym samym priorytecie.

Gdy wiadomo już, z którego obszaru wymiany skorzystać, wywoływana jest funkcja scan_swap_map()
, która zwraca numer znajdującego sie w tym obszarze slotu do zaalokowania. Na podstawie numeru obszaru wymiany i numeru slotu strony tworzony jest identyfikator wymienianej strony, który jest zwracany.
Za zwolnienie danego slotu odpowiada funkcja
__swap_free(swp_entry_t entry, unsigned short count)
która wykonuje następujące operacje:
uaktualnia pola lowest_bit i highest_bit deskryptora obszaru wymiany
zmniejsza licznik swap_map[nr_zwalnianego_slotu]
jeśli swap_map[nr_zwalnianego_slotu] == 0 oznacza to, że nikt nie korzysta z zawartości slotu, a więc liczba wolnych slotów zwiększa się (nr_swap_pages--)
Źródła omawianych tutaj struktur i funkcji można znaleźć w plikach:
mm/swapfile.c
mm/swap.c