Janusz Kuligowski, nr albumu 171815
Grudzień 2001
Skupiam się w nim na wersji systemu dla procesorów architektury i386. O ile podstawowe pojęcia nie zależą od wybranej platformy sprzętowej (np. ramka, strona, pamięć wirtualna), o tyle pewne parametry są z nią ściśle związane.
Należy więc zauważyć, że dla omawianej rodziny procesorów przestrzeń adresowa jest ograniczona do 4 GB, ze względu na wykorzystywanie do zapisania adresu 32 bitów.
W źródłach termin "page" bywa używany zarówno w odniesieniu do ramek, jak i stron - zatem różne przeze mnie tłumaczenie przeze mnie słowa page nie wynika z niekonsekwencji, ale jest następstwem używania w polskiej terminologii odrębnych terminów - dla przypomnienia:
Znajduje się tu także definicja stałej PAGE_MASK - maski bitowej,
mającej jedynki na pozycjach oznaczających bity numeru strony i zera na
pozycjach bitów przesunięcia (offsetu) - czyli adresu w obrębie strony:
#define PAGE_MASK ( (PAGE_SIZE-1)) </PRE> .
Przykład zastosowania tej maski w praktyce znajduje się już w tym pliku parę linijek niżej:
<PRE> #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) )) </PRE>
- jest to makro ustawiające addr na początek nowej ramki; stała PAGE_MASK jest wykorzystana do wyzerowania bitów offsetu adresu.)
Zdefiniowanie stałej PAGE_SIZE na liczbę 12 oznacza, że pamięć dzielimy na ramki wielkości 4kB.
Kolejną ważną stałą jest:
#define __PAGE_OFFSET (0xC0000000)
#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET),
co jak widać ustala wielkość pamięci użytkownika na 3GB - zatem wirtualna przestrzeń adresowa jądra zajmuje tylko 1GB,
co umożliwia wykorzystanie co najwyżej 950MB fizycznej pamięci.
typedef struct page {struct list_head list;
struct address_space *mapping;
unsigned long index;
struct page *next_hash;
atomic_t count;
unsigned long flags;
struct list_head lru;
unsigned long age;
wait_queue_head_t wait;
struct page **pprev_hash;
struct buffer_head * buffers;
void * virtual;
struct zone_struct *zone;
} mem_map_t;
Zostaje ona zainicjowana w pliku /mm/page_alloc.c w funkcji free_area_init_core(). Początkowo wszystkie ramki mają ustawioną flagę PG_reserved, zwalniane są dopiero w funkcji free_all_bootmem() z pliku /mm/bootmem.c. (W pliku tym znajdują się też inne funkcje używane przy starcie systemu.)list;
/* wskaźniki listowe */*mapping;
/* wskaźnik I-węzła mapowanego pliku */index;
/* pozycja w mapowanym pliku */*next_hash;
/* następna ramka o tym samym kluczu w tablicy haszującej */count;
/* licznik odwołań */flags;
/* flagi bitowe */lru;
/* wskaźniki listy zwalniania, chronione przez pagemap_lru_lock */age;
/* licznik wieku strony w ramce */wait;
/* ramka zablokowana? ustaw się w kolejce... */**pprev_hash;
/* poprzednia ramka o tym samym kluczu w tablicy haszującej */* buffers;
/* wskaźnik na listę nagłówków buforów (jeżeli ramka jest wykorzystywana na bufory) */virtual;
/* wirtualny adres na potrzeby jądra */*zone;
/* strefa pamięci, w której ramka się znajduje */
struct list_head
*next, *prev;
typedef struct zone_struct {spinlock_t
lock;unsigned long
free_pages;unsigned long
inactive_clean_pages;unsigned long
inactive_dirty_pages;unsigned long
pages_min, pages_low, pages_high;
struct list_head
inactive_clean_list;free_area_t
free_area[MAX_ORDER];
struct pglist_data
*zone_pgdat;struct page
*zone_mem_map;unsigned long
zone_start_paddr;unsigned long
zone_start_mapnr;
char
*name;unsigned long
size;
} zone_t;
semaforek */
lock; /*
#define ZONE_DMA 0
#define ZONE_NORMAL 1
#define ZONE_HIGHMEM 2
#define MAX_NR_ZONES 3
#define get_page(p) atomic_inc(&(p)->count) /* pobieramy */
#define put_page(p) __free_page(p) /* oddajemy */
#define put_page_testzero(p) atomic_dec_and_test(&(p)->count)
#define page_count(p) atomic_read(&(p)->count)
#define set_page_count(p,v) atomic_set(&(p)->count, v)
Aby umożliwić wybór strony do wyswapowania na dysk ramki mapujące pliki używają bitu PG_referenced, który jest ustawiany zawsze wtedy, gdy system odwołuje się do ramki przez tablicę haszującą.
PG_error jest ustawiana w przypadku wystąpienia błędu wejścia/wyjścia przy obsłudze tej ramki.
Strony z ustawionym bitem PG_highmem nie są stale umieszczone w wirtualnej przestrzeni adresowej jądra - muszą byc "kmapped" (przyłączane) oddzielnie przed wykonaniem operacji wejścia/wyjścia.
Oto część flag:
#define PG_locked
0 /* Zablokowana. Nie dotykaj! */#define PG_error
1 /* Był błąd We/Wy */#define PG_referenced
2 /* Były odwołania */#define PG_uptodate
3 /* Zgodna ze swoim odwzorowaniem na dysku */#define PG_dirty
4 /* Była modyfikowana */#define PG_active
6 /* Aktywna */#define PG_slab
8 /* Wykorzystywana na pamięć jądra */#define PG_inactive_clean
11 /* Nieaktywna, niemodyfikowana (może być odesłana na dysk) */
#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags)
#define SetPageUptodate(page) set_bit(PG_uptodate, &(page)->flags)
#define ClearPageUptodate(page) clear_bit(PG_uptodate, &(page)->flags)
#define PageDirty(page) test_bit(PG_dirty, &(page)->flags)
#define SetPageDirty(page) set_bit(PG_dirty, &(page)->flags)
#define ClearPageDirty(page) clear_bit(PG_dirty, &(page)->flags)
#define PageLocked(page) test_bit(PG_locked, &(page)->flags)
#define LockPage(page) set_bit(PG_locked, &(page)->flags)
#define TryLockPage(page) test_and_set_bit(PG_locked, &(page)->flags)
#define PageChecked(page) test_bit(PG_checked, &(page)->flags)
#define SetPageChecked(page) set_bit(PG_checked, &(page)->flags)
struct address_space {struct list_head clean_pages ;
struct list_head dirty_pages ;
struct list_head locked_pages ;
unsigned long nrpages ;
struct address_space_operations *a_ops ;
struct inode *host ;
struct vm_area_struct *i_mmap ;
struct vm_area_struct *i_mmap_shared ;
spinlock_t i_shared_lock ;
int gfp_mask ;
};
clean_pages
/* lista czystych stron */dirty_pages
/* lista zmodyfikowanych stron */locked_pages
/* lista zablokowanych stron */nrpages
/* liczba wszystkich stron */*a_ops
/* operacje, które można wykonywać na tej strukturze */*host
/* właścicel: inode, block_device */*i_mmap
/* lista prywatnych mapowań */*i_mmap_shared
/* lista współdzielonych mapowań */i_shared_lock
/* semafor */gfp_mask
/* sposób alokacji pamięci */
Pojawia się tutaj bardzo ciekawa struktura address_space_operations.
Struktury z rodziny ,,struktur operacji''
(o nazwach: ..._operations) przywołują na myśl obiektowe podejście do programowania - gdyż są to wskaźniki do funkcji,
mogących wykonywać operacje na polach struktury. Są one nawet nazywane przez autorów metodami :
struct address_space_operations {int (*writepage)(struct page *);
int (*readpage)(struct file *, struct page *);
int (*sync_page)(struct page *);
int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
int (*bmap)(struct address_space *, long);
};