[文档名称] 〈V1.0〉
linux 的内存管理分为四个大部分:
1、初始化过程中内存的建立及到伙伴系统的转移;
2、伙伴系统、slab 分配器、非连续内存的管理;
3、进程地址空间的内存管理;
4、内存回收;
1 1、初始化过程中内存的建立及到伙伴系统的转移;
把所有 SDRAM 可用的(去悼内核代码、页目录)以页为单位分成多个页,每个页一个比特,提供初始阶段内
存的分配和释放;在 boot.S 里只是映射了 3M,现在要把所有 SDRAM 映射到内核空间、调用平台的内存映射
函数以完成外设空间的映射;根据物理内存的布局也就是 SDRAM 的结点布局对多个结点及结点的管理区作初
化,最后把所有剩余的页交给页框分配器,同时也完成了页框分配器的初始化。
相关数据结构:
meminfo 是这个类型的变量,是通过 bootloader 传递的参数建立的,一个内存的分为多个结点
struct meminfo {
//表示有多少个结点
int nr_banks;
struct {
//sdram 的开始地址和大小。
unsigned long start;
unsigned long size;
//相应 sdram 的结点号。
int
node;
} bank[NR_BANKS];
};
每个平台都要根据实际情况填充这里面的成员。
struct machine_desc {
unsigned int nr; /* architecture number */
unsigned int phys_ram; /* start of physical ram */
unsigned int phys_io; /* start of physical io */
unsigned int io_pg_offst; /* byte offset for io
* page tabe entry */
const char *name; /* architecture name */
unsigned long boot_params; /* tagged list */
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned int reserve_lp0 :1; /* never has lp0 */
unsigned int reserve_lp1 :1; /* never has lp1 */
unsigned int reserve_lp2 :1; /* never has lp2 */
unsigned int soft_reboot :1; /* soft reboot */
void (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
第 2 页 共 62 页
2
[文档名称] 〈V1.0〉
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
};
//每个节点对应一个这样的结构。
typedef struct pglist_data {
struct zone node_zones[MAX_NR_ZONES];
//根据管理区的不同共有三个原素,每个里面存放了系统中所在节点的与数组索引一样类型的管理区指针。
struct zonelist node_zonelists[GFP_ZONETYPES];
//管理区数量。
int nr_zones;//0
#ifdef CONFIG_FLAT_NODE_MEM_MAP
//把当前 SDRAM 分成 N 个页面,每个页面分配一个 struct page 结构,node_mem_map 是数组头。
struct page *node_mem_map;
#endif
//bootmem 使用,已级静态分配并赋值。
struct bootmem_data *bdata;
//SDRAM 物理地址的 PFN。
unsigned long node_start_pfn;
//SDRAM 总的大小(去悼洞)。
unsigned long node_present_pages; /* total number of physical pages */
//SDRAM 总的大小包括洞。
unsigned long node_spanned_pages; /* total size of physical page
range, including holes */
//当前 SDRAM 对应的节点 ID。
int node_id;
//下一个节点。
struct pglist_data *pgdat_next;
//kswapd 相关的。
wait_queue_head_t kswapd_wait;
struct task_struct *kswapd;
int kswapd_max_order;//0
} pg_data_t;
struct zone {
//空闲页的数目。
unsigned long free_pages;
//分别是保留页、回收下界、回收上界。
unsigned long pages_min, pages_low, pages_high;
//在处理内存不足的情况下,每个管理区必须保留的页框数。
unsigned long lowmem_reserve[MAX_NR_ZONES];
#ifdef CONFIG_NUMA
struct per_cpu_pageset *pageset[NR_CPUS];
第 3 页 共 62 页
3
[文档名称] 〈V1.0〉
#else
//每 CPU 页框高速缓存。
struct per_cpu_pageset pageset[NR_CPUS];
#endif
/*
* free areas of different sizes
*/
spinlock_t lock;
//管理区空闲页框块。
struct free_area free_area[MAX_ORDER];
ZONE_PADDING(_pad1_)
/* Fields commonly accessed by the page reclaim scanner */
spinlock_t lru_lock;
struct list_head active_list;
struct list_head inactive_list;
unsigned long nr_scan_active;
unsigned long nr_scan_inactive;
unsigned long nr_active;
unsigned long nr_inactive;
unsigned long pages_scanned;
/* since last reclaim */
int all_unreclaimable; /* All pages pinned */
atomic_t reclaim_in_progress;
int temp_priority;
int prev_priority;
ZONE_PADDING(_pad2_)
//进程等待队列的散列表。
wait_queue_head_t * wait_table;
//散列表的大小。
unsigned long wait_table_size;
unsigned long wait_table_bits;
//所属的结点描述符。
struct pglist_data *zone_pgdat;
//管理区内第一个页描述符的指针。
struct page *zone_mem_map;
/* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */
//管理区开始物理地址的 PFN。
unsigned long zone_start_pfn;
//当前管理区总大小。
unsigned long spanned_pages; /* total size, including holes */
第 4 页 共 62 页
4
//去悼洞后的总大小。
unsigned long present_pages; /* amount of memory (excluding holes) */
[文档名称] 〈V1.0〉
//管理区名字。
char *name;
} ____cacheline_maxaligned_in_smp;
typedef struct bootmem_data {
//SDRAM 的开始物理地址。
unsigned long node_boot_start;
//SDRAM 的结束 PFN。
unsigned long node_low_pfn;
//SDRAM 在 bootmem 管理区的起始虚拟地址,每个比特代一个页,记录 SDRAM 的使用情况。
void *node_bootmem_map;//vm end of kernel code
unsigned long last_offset;
unsigned long last_pos;
unsigned long last_success; /* Previous allocation point. To speed
* up searching */
} bootmem_data_t;
提示:每个 SDRAM 要分为多个结点,每个结点要分为多个管理区。struct meminfo 代表 SDRAM,struct pglist_data 代表结点,struct zone
代表管理区。
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc;
char *from = default_command_line;
//对 CPU 相关的变里初始化。
setup_processor();
//获取平台描述符。
mdesc = setup_machine(machine_arch_type);
machine_name = mdesc->name;
if (mdesc->soft_reboot)
reboot_setup("s");
//load 参数存放位置。
if (mdesc->boot_params)
tags = phys_to_virt(mdesc->boot_params);
/*
* If we have the old style parameters, convert them to
* a tag list.
*/
//如果可能把老格式转成新格式。
if (tags->hdr.tag != ATAG_CORE)
convert_to_tag_list(tags);
if (tags->hdr.tag != ATAG_CORE)
tags = (struct tag *)&init_tags;
第 5 页 共 62 页
5
[文档名称] 〈V1.0〉
//这个成员是空。
if (mdesc->fixup)
mdesc->fixup(mdesc, tags, &from, &meminfo);
else
printk("fixup is null\n");
//分析 loader 放在参数区里放的参数。
if (tags->hdr.tag == ATAG_CORE) {
if (meminfo.nr_banks != 0)
squash_mem_tags(tags);
parse_tags(tags);
}
init_mm.start_code = (unsigned long) &_text;
init_mm.end_code = (unsigned long) &_etext;
init_mm.end_data = (unsigned long) &_edata;
init_mm.brk = (unsigned long) &_end;
memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
printk("parse_cmdline before cmdline [%s]\n",from);
parse_cmdline(cmdline_p, from);
printk("parse_cmdline before cmdline [%s]\n",*cmdline_p);
printk("meminfo
[%d][%d][%d][%d]\n",meminfo.nr_banks,meminfo.bank[0].start,meminfo.bank[0].size,meminfo.bank[0].node);
//对物内存作了全方位的初始化。
paging_init(&meminfo, mdesc);
request_standard_resources(&meminfo, mdesc);
//初始化中断和异常的栈,只是三个字啊。
cpu_init();
/*
* Set up various architecture-specific pointers
*/
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
//配置控制台。
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
printk("conswitchp=%p\n",conswitchp);
#endif
#endif
}
page_init 根据 SDRAM 内结点的物理布局、平台相关的变量,对物理内存作全方位的初化。
第 6 页 共 62 页
6
void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc)
{
[文档名称] 〈V1.0〉
void *zero_page;
int node;
//初始化 SDRAM,函数返回后,可以用 bootmem 分配内存了。
bootmem_init(mi);
memcpy(&meminfo, mi, sizeof(meminfo));
/*
* allocate the zero page. Note that we count on this going ok.
*/
zero_page = alloc_bootmem_low_pages(PAGE_SIZE);
/*
* initialise the page tables.
*/
//初始化内核的页表,把所在 SDRAM 都映射到相应的虚拟空间。
memtable_init(mi);
//调用平台的映射 IO 的函数,把所有 IO 寄存器都映射的相应的虚拟空间。
if (mdesc->map_io)
{
printk("[setup_arch -> machine_desc.map_io]\n");
mdesc->map_io();
}
local_flush_tlb_all();
/*
* initialise the zones within each node
*/
//初始化所有节点
for_each_online_node(node) {
unsigned long zone_size[MAX_NR_ZONES];
unsigned long zhole_size[MAX_NR_ZONES];
struct bootmem_data *bdata;
pg_data_t *pgdat;
int i;
/*
* Initialise the zone size information.
*/
for (i = 0; i < MAX_NR_ZONES; i++) {
zone_size[i] = 0;
zhole_size[i] = 0;
}
pgdat = NODE_DATA(node);
bdata = pgdat->bdata;
/*
* The size of this node has already been determined.
* If we need to do anything fancy with the allocation
* of this memory to the zones, now is the time to do
第 7 页 共 62 页
7
[文档名称] 〈V1.0〉
* it.
*/
zone_size[0] = bdata->node_low_pfn -
(bdata->node_boot_start >> PAGE_SHIFT);
/*
* If this zone has zero size, skip it.
*/
if (!zone_size[0])
continue;
/*
* For each bank in this node, calculate the size of the
* holes. holes = node_size - sum(bank_sizes_in_node)
*/
zhole_size[0] = zone_size[0];
for (i = 0; i < mi->nr_banks; i++) {
if (mi->bank[i].node != node)
continue;
zhole_size[0] -= mi->bank[i].size >> PAGE_SHIFT;
}
/*
* Adjust the sizes according to any special
* requirements for this machine type.
*/
arch_adjust_zones(node, zone_size, zhole_size);
//按照管理区参数,对结点作初始化。
free_area_init_node(node, pgdat, zone_size,
bdata->node_boot_start >> PAGE_SHIFT, zhole_size);
}
/*
* finish off the bad pages once
* the mem_map is initialised
*/
memzero(zero_page, PAGE_SIZE);
empty_zero_page = virt_to_page(zero_page);
flush_dcache_page(empty_zero_page);
}
static void __init bootmem_init(struct meminfo *mi)
{
struct node_info node_info[MAX_NUMNODES], *np = node_info;
unsigned int bootmap_pages, bootmap_pfn, map_pg;
int node, initrd_node;
printk("MAX_NUMNODES %d\n",MAX_NUMNODES);
//返回映射物理内存需要的页数,并把所有 SDRAM 信息填充到 node_info.
bootmap_pages = find_memend_and_nodes(mi, np);
//查找内核所在 SDRAM 结点,把此结点上可用的第一个 4KPFN 返回,bootmem 从这里开始使用。
第 8 页 共 62 页
8