Linux内核对于内存的管理摒弃了i386复杂的段式管理,而是采用了页式管理。Linux把整个的物理内存空间化为成一个个单独的页面,每页占4K空间大小。Linux把所有页面链接到一个全局的数组mem_map[]中,mem_map的每一个元素都是一个指针指向page数据结构。系统中的每个物理页面都对应着一个page数据结构,并根据需要把这些页面划分为不同的管理区:ZONE_DMA,ZONE_NORMAL和ZONE_HIGHMEM(用于物理内存超过1G空间的地址)。
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 !! */
struct page **pprev_hash;/* Complement to *next_hash. */
struct buffer_head * buffers;/* Buffer maps us to a disk block. */
/*
* On machines where all RAM is mapped into kernel address space,
* we can simply calculate the virtual address. On machines with
* highmem some memory is mapped into kernel virtual memory
* dynamically, so we need a place to store that address.
* Note that this field could be 16 bits on x86 ... ;)
*
* Architectures with slow multiplication can define
* WANT_PAGE_VIRTUAL in asm/page.h
*/
#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL)
void *virtual;/* Kernel virtual address (NULL if
not kmapped, ie. highmem) */
#endif /* CONFIG_HIGMEM || WANT_PAGE_VIRTUAL */
} mem_map_t;
DMA管理区是单独进行管理的,不经过MMU映射,而一般的外设都对地址空间有一定的限制。地址空间不能太大,而且要求地址连续。
每个管理区都对应着一个zone_struct结构,这个结构体中有一组空闲队列free_area_t。这些队列中要有一个队列保持一些离散的物理页面,另一个队列要保持长度为2的指数的连续物理页面。
typedef struct zone_struct {
/*
* Commonly accessed fields:
*/
spinlock_tlock;
unsigned longfree_pages;
unsigned longpages_min, pages_low, pages_high;
intneed_balance;
/*
* free areas of different sizes
*/
free_area_tfree_area[MAX_ORDER];
/*
* wait_table-- the array holding the hash table
* wait_table_size-- the size of the hash table array
* wait_table_shift-- wait_table_size
* == BITS_PER_LONG (1 << wait_table_bits)
*
* The purpose of all these is to keep track of the people
* waiting for a page to become available and make them
* runnable again when possible. The trouble is that this
* consumes a lot of space, especially when so few things
* wait on pages at a given time. So instead of using
* per-page waitqueues, we use a waitqueue hash table.
*
* The bucket discipline is to sleep on the same queue when
* colliding and wake all in that wait queue when removing.
* When something wakes, it must check to be sure its page is
* truly available, a la thundering herd. The cost of a
* collision is great, but given the expected load of the
* table, they should be so rare as to be outweighed by the
* benefits from the saved space.
*
* __wait_on_page() and unlock_page() in mm/filemap.c, are the
* primary users of these fields, and in mm/page_alloc.c
* free_area_init_core() performs the initialization of them.
*/
wait_queue_head_t* wait_table;
unsigned longwait_table_size;
unsigned longwait_table_shift;
/*
* Discontig memory support fields.
*/
struct pglist_data*zone_pgdat;
struct page*zone_mem_map;
unsigned longzone_start_paddr;
unsigned longzone_start_mapnr;
/*
* rarely used fields:
*/
char*name;
unsigned longsize;
} zone_t;