物理ページ管理(Buddy System)
全物理ページの管理テーブル
mem_map_t *mem_map <-- struct page *
+-------------------+
| 0 struct page | Idx = physical_address >> 12
| | (ページフレーム#がIndexとなる。1Page 4KByte)
+-------------------+
| 1 |
| |
+-------------------+
:
+-------------------+
| max_mapnr - 1 | zone:所属zoneへのポインタ
| | virtual:該当ページの仮想アドレス(0xc0000000 + 物理アドレス)
+-------------------+ free_area_init_coreで設定
Zone
物理メモリを3種類(DMA,NORMAL,HIGHMEM)のZoneに分けて管理する。
Zone内のページは物理的に連続ページとなる。
各Zoneは2^n Pageの単位で物理Pageの割り当てを行う。
各ページは必ずどれかのZoneに属する。
contig_page_data
+-----------------+ node_zones[]
| ZONE_DMA |
| |
| |
| |
+-----------------+
| ZONE_NORMAL | free_area[MAX_ORDER]:2^n PageSize毎にBitmapで使用状況を管理
| | zone_mem_map:Zone先頭Pageの該当mem_mapへのポインタ
| | zone_start_mapnr:Zone先頭Pageの該当mem_mapのindex
| | zone_start_paddr:Zone先頭Pageの物理アドレス
+-----------------+
| ZONE_HIGHMEM | ^
| | |
| | |
| | |
+-----------------+ node_zonelists[]
| ZONE_DMA * | |
+-----------------+ |
| ZONE_NORMAL * |----+
+-----------------+
| ZONE_HIGHMEM * |
+-----------------+
| NULL |
+-----------------+
:
:
free_area[] Example
Page bitmap size(exp. 4096Page)
free_area[0].map 1 255Byte
free_area[1].map 2 127Byte
free_area[2].map 4 63Byte
:
free_area[7].map 128 1Byte
:
free_area[MAX_ORDER(10)].map 1024 0Byte
Total 502Byte???
pg_data_t contig_page_data
後述
[初期化]
start_kernel()
:
setup_arch()
:
paging_init()
pagetable_init()
PTEを設定
仮想アドレス0xc0000000〜__va(max_low_pfn*PAGE_SIZE)を
物理アドレス0x00000000へストレートマッピング
max_low_pfn:RAMの最後の物理ページ番号
<-- e820hのBIOSコールで取得したメモリマップから計算される。
メモリマップの内容は起動時にDmesgに出力されている。
:
free_area_init()
free_area_init_core()
全ページ分の管理テーブル用バッファを確保し、mem_mapに登録
全ページReservedにする。
ZoneListもここで初期化する。
各ページの仮想アドレス(page->virtual)もここで設定
[ページの割り当て]
alloc_pages(gfp_mask, order)
指定したZoneのfree listから 2^order Pageを取得する。
Zoneはgfp_maskの種類によって選択される。
取得したらZoneのfree_areaのBitmapを立てる。
page->countも1にする。
該当物理ページのmem_mapポインタを返す。
[メモリマップ例]
BIOS-provided physical RAM map:
BIOS-e820: 0000000000000000 - 000000000009f800 (usable) <-- RAM領域
BIOS-e820: 000000000009f800 - 00000000000a0000 (reserved)
BIOS-e820: 00000000000e2800 - 0000000000100000 (reserved)
BIOS-e820: 0000000000100000 - 000000000bff0000 (usable) <-- RAM領域
BIOS-e820: 000000000bff0000 - 000000000bfff800 (ACPI data)
BIOS-e820: 000000000bfff800 - 000000000c000000 (ACPI NVS)
BIOS-e820: 00000000fff80000 - 0000000100000000 (reserved)
On node 0 totalpages: 49136
zone(0): 4096 pages. <-- DMA 0x00000000 - 0x01000000
zone(1): 45040 pages. <-- NORMAL 0x01000000 - 0x0bff0000
zone(2): 0 pages. <-- HIGHMEM
[関連構造体]
typedef struct page {
struct list_head list; /* ->mapping has some page lists. */
struct address_space *mapping; /* The inode (or ...) we belong to. */
unsigned long index; /* Our offset within mapping. */
struct page *next_hash; /* Next page sharing our hash bucket in
the pagecache hash table. */
atomic_t count; /* Usage count, see below. */
unsigned long flags; /* atomic flags, some possibly
updated asynchronously */
struct list_head lru; /* Pageout list, eg. active_list;
protected by pagemap_lru_lock !! */
wait_queue_head_t wait; /* Page locked? Stand in line... */
struct page **pprev_hash; /* Complement to *next_hash. */
struct buffer_head * buffers; /* Buffer maps us to a disk block. */
void *virtual; /* Kernel virtual address (NULL if
not kmapped, ie. highmem) */
struct zone_struct *zone; /* Memory zone we are in. */
} mem_map_t;
物理ページの管理構造体 (1Pageにつき1エントリ)
index mapping内でのオフセット(Page単位)
virtual 該当物理ページに対応するカーネル仮想アドレスを示す
(0xc0000000からストレートマップされた値)
<-- Kernel2.6ではarchによってはなくなる
virtualがない場合、page_address()は
page - mem_mapからページフレーム#を求め物理アドレスを計算する。
0xc0000000を加えてカーネル仮想アドレスを取得する。
この作りからもstruct pageは起動時に作成されたmem_mapにしか
存在しないことがわかる。
(後から動的にstruct pageが確保されるようなことはない。)
typedef struct zone_struct {
/*
* Commonly accessed fields:
*/
spinlock_t lock;
unsigned long free_pages;
unsigned long pages_min, pages_low, pages_high;
int need_balance;
/*
* free areas of different sizes
*/
free_area_t free_area[MAX_ORDER];
/*
* Discontig memory support fields.
*/
struct pglist_data *zone_pgdat;
struct page *zone_mem_map;
unsigned long zone_start_paddr;
unsigned long zone_start_mapnr;
/*
* rarely used fields:
*/
char *name;
unsigned long size;
} zone_t;
typedef struct pglist_data {
zone_t node_zones[MAX_NR_ZONES];
zonelist_t node_zonelists[GFP_ZONEMASK+1];
int nr_zones;
struct page *node_mem_map;
unsigned long *valid_addr_bitmap;
struct bootmem_data *bdata;
unsigned long node_start_paddr;
unsigned long node_start_mapnr;
unsigned long node_size;
int node_id;
struct pglist_data *node_next;
} pg_data_t;