日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux内存管理分析 二,linux内存管理分析【二】

發布時間:2024/1/23 linux 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux内存管理分析 二,linux内存管理分析【二】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

為建立內存管理系統,在內核初始化過程中調用了下面幾個函數:

init/main.c

asmlinkage?void?__init?start_kernel(void)

{

......

初始化持久映射與臨時映射的一些信息,后面持久映射和臨時映射一節將詳細講解

page_address_init();

setup_arch是特定于體系架構的函數,負責初始化自舉分配器和內核頁表等。

setup_arch(&command_line);

......

初始化per_cpu機制的一些結構,將.data.percpu段中的數據拷貝到每個CPU的數據段中

setup_per_cpu_areas();

......

建立結點和內存域之間的關系

build_all_zonelists(NULL);

......

停用自舉分配器bootmem,遷移到實際的內存管理中,初始化slab分配器,初始化進程虛擬地址空間管理結構

mm_init();

......

為每一個內存區域分配per_cpu_pageset結構并初始化其成員

setup_per_cpu_pageset();

......

}

【start_kernel--->setup_arch】

arch/arm/kernel/setup.c

void?__init?setup_arch(char?**cmdline_p)

{

struct?machine_desc?*mdesc;

內核參數可以通過平坦設備樹或者tags由bootloader傳遞給內核。

每一個機器平臺都由一個struct?machine_desc結構來描述,內核所支持的所有平臺對應的machine_desc結構都包含在段.init.arch.info的?__arch_info_begin?到?__tagtable_end之間。但每一個平臺都有其唯一的機器碼machine_arch_type,可通過機器碼在段.init.arch.info中找到對應的平臺描述結構。?函數setup_machine_tags就是根據機器碼找到對應的平臺描述結構,并且分析內核參數中內存相關的信息,用以初始化內存塊管理結構membank。

mdesc?=?setup_machine_fdt(__atags_pointer);

if?(!mdesc)

mdesc?=?setup_machine_tags(machine_arch_type);

machine_desc?=?mdesc;

machine_name?=?mdesc->name;

根據mdesc->dma_zone_size設置DMA區域的大小arm_dma_zone_size,和DMA區域的結束地址arm_dma_limit

setup_dma_zone(mdesc);

結構struct?mm_struct?管理進程的虛擬地址空間,所有內核線程都使用共同的地址空間,因為他們都是用相同的地址映射,這個地址空間由init_mm來描述。_text?和_etext表示內核鏡像代碼段的其實和結束位置,_etext和_edata之間是已初始化數據段,_edata到_end是未初始化數據段等,_end之后便是堆區。

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;

內核命令行參數在函數setup_machine_tags獲取并保存在了boot_command_line中

strlcpy(cmd_line,?boot_command_line,?COMMAND_LINE_SIZE);

*cmdline_p?=?cmd_line;

分析命令行參數,主要關注一些與內存相關的東西

parse_early_param();

將內存塊按從小到大排序

sort(&meminfo.bank,?meminfo.nr_banks,?sizeof(meminfo.bank[0]),?meminfo_cmp,?NULL);

掃描各個內存塊,檢測低端內存的最大值arm_lowmem_limit,設置高端內存起始值的虛擬地址high_memory

sanity_check_meminfo();

將所有內存塊添加到結構memblock的memory區中,將已使用的內存添加到reserved區中去。

arm_memblock_init(&meminfo,?mdesc);

創建內核頁表,初始化自舉分配器

paging_init(mdesc);

內核中將許多物理資源用struct?resource結構來管理,下面函數就是將IO內存作為resource注冊到內核

request_standard_resources(mdesc);

......

如果內核命令行中有預留用于內核crash是的轉存空間,就將這些存儲空間標記為已分配????????reserve_crashkernel();

......

}

【start_kernel--->setup_arch--->setup_machine_tags】

arch/arm/kernel/setup.c

static?struct?machine_desc?*?__init?setup_machine_tags(unsigned?int?nr)

{

struct?tag?*tags?=?(struct?tag?*)&init_tags;

struct?machine_desc?*mdesc?=?NULL,?*p;

char?*from?=?default_command_line;

init_tags.mem.start?=?PHYS_OFFSET;

下面循環根據機器號在段.init.arch.info中尋找對應的machine_desc結構

for_each_machine_desc(p)

if?(nr?==?p->nr)?{

printk("Machine:?%s\n",?p->name);

mdesc?=?p;

break;

}

......

Bootloader傳入的參數地址存放在__atags_pointer中

if?(__atags_pointer)

tags?=?phys_to_virt(__atags_pointer);

else?if?(mdesc->atag_offset)

tags?=?(void?*)(PAGE_OFFSET?+?mdesc->atag_offset);

......

內核參數是由struct?tag來管理,其中第一個tag類型必然是ATAG_CORE

if?(tags->hdr.tag?!=?ATAG_CORE)?{

......

tags?=?(struct?tag?*)&init_tags;內核提供的一個默認參數列表

}

函數mdesc->fixup中一般會獲取內存塊的信息

if?(mdesc->fixup)

mdesc->fixup(tags,?&from,?&meminfo);

if?(tags->hdr.tag?==?ATAG_CORE)?{

如果內存塊已經初始化,就將參數列表中關于內存的參數標記為ATAG_NONE

if?(meminfo.nr_banks?!=?0)

squash_mem_tags(tags);

將參數列表拷貝到一個靜態數組atags_copy中

save_atags(tags);

分析內核參數,后面細講

parse_tags(tags);

}

將解析出來的內核命令行信息拷貝到靜態數組boot_command_line中。在內核啟動期間用了很多靜態存儲空間,它們前面綴有__initdata,像這樣的空間在內核啟動起來后將被釋放

strlcpy(boot_command_line,?from,?COMMAND_LINE_SIZE);

return?mdesc;

}

【start_kernel--->setup_arch--->setup_machine_tags--->parse_tags】

arch/arm/kernel/setup.c

static?void?__init?parse_tags(const?struct?tag?*t)

{

遍歷參數列表中每一個參數結構

for?(;?t->hdr.size;?t?=?tag_next(t))

if?(!parse_tag(t))

......

}

【start_kernel--->setup_arch--->setup_machine_tags--->parse_tags--->parse_tag】

arch/arm/kernel/setup.c

static?int?__init?parse_tag(const?struct?tag?*tag)

{

extern?struct?tagtable?__tagtable_begin,?__tagtable_end;

struct?tagtable?*t;

參數類型多種多樣解析方式也各不相同,所有針對每一種參數類型都有一個對應的解析函數,這些解析函數和其參數類型由結構struct?tagtable來管理。這些結構都存放在段.init.tagtable的__tagtable_begin和__tagtable_end之間。

for?(t?=?&__tagtable_begin;?t?

if?(tag->hdr.tag?==?t->tag)?{

t->parse(tag);

break;

}

return?t?

}

參數ATAG_MEM的解析函數定義如下:

arch/arm/kernel/setup.c

static?int?__init?parse_tag_mem32(const?struct?tag?*tag)

{

return?arm_add_memory(tag->u.mem.start,?tag->u.mem.size);

}

__tagtable(ATAG_MEM,?parse_tag_mem32);

【parse_tag_mem32--->arm_add_memory】

從ATAG_MEM參數中獲取內存信息,初始化內存塊管理結構

int?__init?arm_add_memory(phys_addr_t?start,?unsigned?long?size)

{

struct?membank?*bank?=?&meminfo.bank[meminfo.nr_banks];

if?(meminfo.nr_banks?>=?NR_BANKS)?{

printk(KERN_CRIT?"NR_BANKS?too?low,?"

"ignoring?memory?at?0x%08llx\n",?(long?long)start);

return?-EINVAL;

}

size?-=?start?&?~PAGE_MASK;

bank->start?=?PAGE_ALIGN(start);

#ifndef?CONFIG_LPAE

if?(bank->start?+?size?start)?{

size?=?ULONG_MAX?-?bank->start;

}

#endif

bank->size?=?size?&?PAGE_MASK;

if?(bank->size?==?0)

return?-EINVAL;

meminfo.nr_banks++;

return?0;

}

【start_kernel--->setup_arch--->sanity_check_meminfo】

arch/arm/mm/mmu.c

void?__init?sanity_check_meminfo(void)

{

int?i,?j,?highmem?=?0;

遍歷每一個內存塊

for?(i?=?0,?j?=?0;?i?

struct?membank?*bank?=?&meminfo.bank[j];

*bank?=?meminfo.bank[i];

if?(bank->start?>?ULONG_MAX)

highmem?=?1;

#ifdef?CONFIG_HIGHMEM

vmalloc_min在文件arch/arm/mm/mmu.c中定義,它定義了高端內存的起始位置。PAGE_OFFSET是物理位置的起始處。如果內存塊起始位置大于vmalloc_min,表示存在高端內存。如果內存擴展超過32位,它就有可能小于PAGE_OFFSET。

if?(__va(bank->start)?>=?vmalloc_min?||

__va(bank->start)?

highmem?=?1;

標志該內存塊是否處于高端內存中

bank->highmem?=?highmem;

如果該內存塊部分處于高端內存中,部分處于低端內存中就將其分為兩個內存塊。

if?(!highmem?&&?__va(bank->start)?

bank->size?>?vmalloc_min?-?__va(bank->start))?{

if?(meminfo.nr_banks?>=?NR_BANKS)?{

......

}?else?{

memmove(bank?+?1,?bank,

(meminfo.nr_banks?-?i)?*?sizeof(*bank));

meminfo.nr_banks++;

i++;

bank[1].size?-=?vmalloc_min?-?__va(bank->start);

bank[1].start?=?__pa(vmalloc_min?-?1)?+?1;

bank[1].highmem?=?highmem?=?1;

j++;

}

bank->size?=?vmalloc_min?-?__va(bank->start);

}

#else?如果不支持高端內存做如下處理

bank->highmem?=?highmem;

if?(highmem)?{

......

continue;

}

if?(__va(bank->start)?>=?vmalloc_min?||

__va(bank->start)?

......

continue;

}

if?(__va(bank->start?+?bank->size)?>?vmalloc_min?||

__va(bank->start?+?bank->size)?start))?{

unsigned?long?newsize?=?vmalloc_min?-?__va(bank->start);

......

bank->size?=?newsize;

}

#endif

求出低端內存的最大地址值

if?(!bank->highmem?&&?bank->start?+?bank->size?>?arm_lowmem_limit)

arm_lowmem_limit?=?bank->start?+?bank->size;

j++;

}

......

meminfo.nr_banks?=?j;?記錄內存塊數

計算高端內存起始地址,該值不一定等于?vmalloc_min,因為可能沒有高端內存

high_memory?=?__va(arm_lowmem_limit?-?1)?+?1;

memblock_set_current_limit(arm_lowmem_limit);

}

【start_kernel--->setup_arch--->arm_memblock_init】

arch/arm/mm/init.c

void?__init?arm_memblock_init(struct?meminfo?*mi,?struct?machine_desc?*mdesc)

{

int?i;

將所有內存模塊添加到memblock.memory中。結構體memblock在文件mm/memblock.c中定義,如下:

struct?memblock?memblock?__initdata_memblock?=?{

.memory.regions?????????=?memblock_memory_init_regions,

......

.reserved.regions???????=?memblock_reserved_init_regions,

......

};

for?(i?=?0;?i?nr_banks;?i++)

memblock_add(mi->bank[i].start,?mi->bank[i].size);

如果內核在rom中運行就只將它的數據段開始的空間添加到memblock.reserved中,否則將內核代碼段開始的空間添加到memblock.reserved中。

#ifdef?CONFIG_XIP_KERNEL

memblock_reserve(__pa(_sdata),?_end?-?_sdata);

#else

memblock_reserve(__pa(_stext),?_end?-?_stext);

#endif

#ifdef?CONFIG_BLK_DEV_INITRD

如果支持initrd啟動,此時它還不在內存中

if?(phys_initrd_size?&&

!memblock_is_region_memory(phys_initrd_start,?phys_initrd_size))?{

pr_err("INITRD:?0x%08lx+0x%08lx?is?not?a?memory?region?-?disabling?initrd\n",

phys_initrd_start,?phys_initrd_size);

phys_initrd_start?=?phys_initrd_size?=?0;

}

if?(phys_initrd_size?&&

memblock_is_region_reserved(phys_initrd_start,?phys_initrd_size))?{

pr_err("INITRD:?0x%08lx+0x%08lx?overlaps?in-use?memory?region?-?disabling?initrd\n",

phys_initrd_start,?phys_initrd_size);

phys_initrd_start?=?phys_initrd_size?=?0;

}

為inird鏡像預留一塊存儲區

if?(phys_initrd_size)?{

memblock_reserve(phys_initrd_start,?phys_initrd_size);

initrd_start?=?__phys_to_virt(phys_initrd_start);

initrd_end?=?initrd_start?+?phys_initrd_size;

}

#endif

為內核頁表分配存儲空間

arm_mm_memblock_reserve();

......

}

【start_kernel--->setup_arch--->paging_init】

arch/arm/mm/mmu.c

void?__init?paging_init(struct?machine_desc?*mdesc)

{

void?*zero_page;

memblock_set_current_limit(arm_lowmem_limit);

根據不同的arm版本初始化不同的mem_types,該結構存放著頁表的一些屬性相關信息

build_mem_type_table();

將除了內核鏡像、主內存所在虛擬地址之外全部內存的頁表清除掉

prepare_page_table();

為低端內存的所有區域創建內核頁表

map_lowmem();

對DMA區域重新創建頁表

dma_contiguous_remap();

為設備IO空間和中斷向量表創建頁表,并刷新TLB和緩存

devicemaps_init(mdesc);

獲取持久映射區頁表的位置,存儲在pkmap_page_table中

kmap_init();

高64K是用于存放中斷向量表的

top_pmd?=?pmd_off_k(0xffff0000);

分配一個0頁,該頁用于寫時復制機制。

zero_page?=?early_alloc(PAGE_SIZE);

初始化自舉內存分配,后面有專門章節講解

bootmem_init();

empty_zero_page?=?virt_to_page(zero_page);

刷新數據緩存

__flush_dcache_page(NULL,?empty_zero_page);

}

【start_kernel--->setup_arch--->paging_init--->prepare_page_table】

arch/arm/mm/mmu.c

static?inline?void?prepare_page_table(void)

{

unsigned?long?addr;

phys_addr_t?end;

模塊加載的范圍應該是在MODULES_VADDR到MODULES_END之間,MODULES_VADDR在文件arch/arm/include/asm/memory.h中定義,如下:

#define?MODULES_VADDR???????????(PAGE_OFFSET?-?8*1024*1024)

對于arm處理器,該區域在正常內核虛擬地址之下。清除存儲空間在MODULES_VADDR之下的頁表項。

for?(addr?=?0;?addr?

pmd_clear(pmd_off_k(addr));

#ifdef?CONFIG_XIP_KERNEL

addr?=?((unsigned?long)_etext?+?PMD_SIZE?-?1)?&?PMD_MASK;

#endif

for?(?;?addr?

pmd_clear(pmd_off_k(addr));

第一個存儲區存放的是內核鏡像,跳過該區域,即不清除這個區域的頁表

end?=?memblock.memory.regions[0].base?+?memblock.memory.regions[0].size;

if?(end?>=?arm_lowmem_limit)

end?=?arm_lowmem_limit;

for?(addr?=?__phys_to_virt(end);

addr?

pmd_clear(pmd_off_k(addr));

}

【start_kernel--->setup_per_cpu_areas】

每CPU變量(per-?cpu-variable)是一種內核的同步機制。每CPU變量分為靜態變量和動態變量。靜態變量用DEFINE_PER_CPU(type,?name)來定義(CPU變量name,類型為type)。這些靜態變量包含在段.data.percpu中。下面函數就是為每個CPU分配一部分空間用于動態分配per_cpu變量。并為每個CPU拷貝一份.data.percup段中的內容。

mm/percpu.c

void?__init?setup_per_cpu_areas(void)

{

unsigned?long?delta;

unsigned?int?cpu;

int?rc;

rc?=?pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE,

PERCPU_DYNAMIC_RESERVE,?PAGE_SIZE,?NULL,

pcpu_dfl_fc_alloc,?pcpu_dfl_fc_free);

if?(rc?

panic("Failed?to?initialize?percpu?areas.");

數組?__per_cpu_offset中存儲了每個CPU,per_cpu變量區域的偏移,在以后訪問per_cpu變量時將用到。

delta?=?(unsigned?long)pcpu_base_addr?-?(unsigned?long)__per_cpu_start;

for_each_possible_cpu(cpu)

__per_cpu_offset[cpu]?=?delta?+?pcpu_unit_offsets[cpu];

}

【start_kernel--->build_all_zonelists】

mm/page_alloc.c

void?__ref?build_all_zonelists(void?*data)

{

設置current_zonelist_order,它決定備用內存域在pglist_data->node_zonelists中的排列順序

set_zonelist_order();

if?(system_state?==?SYSTEM_BOOTING)?{

初始化備用結點內存域列表?pglist_data->node_zonelists。

__build_all_zonelists(NULL);

mminit_verify_zonelist();打印一些調試信息

當前進程的進程描述結構task_struct中有一個成員mems_allowed,該成員是nodemask_t類型的結構體,這個結構體在文件include/linux/nodemask.h中定義如下:

typedef?struct?{?DECLARE_BITMAP(bits,?MAX_NUMNODES);?}?nodemask_t;

這個結構其實就是定義了一個位域,每個位對應一個內存結點,如果置1表示該節點內存可用。在下面函數中將這個位域中每個位置1。

cpuset_init_current_mems_allowed();

}?else?{

#ifdef?CONFIG_MEMORY_HOTPLUG

if?(data)

setup_zone_pageset((struct?zone?*)data);

#endif

如果內核不是出于啟動過程中,就停止CPU的運行來初始化備用結點內存域列表

stop_machine(__build_all_zonelists,?NULL,?NULL);

}

計算總的空閑內存數

vm_total_pages?=?nr_free_pagecache_pages();

內核通過標記頁的可移動類型來避免產生過多碎片,如果可用內存太少就標記page_group_by_mobility_disabled以禁用這種反碎片機制。

if?(vm_total_pages?

page_group_by_mobility_disabled?=?1;

else

page_group_by_mobility_disabled?=?0;

......

}

【start_kernel--->build_all_zonelists--->__build_all_zonelists】

static?__init_refok?int?__build_all_zonelists(void?*data)

{

int?nid;

int?cpu;

......

遍歷每一個內存結點,初始化他們的備用結點內存域列表

for_each_online_node(nid)?{

pg_data_t?*pgdat?=?NODE_DATA(nid);

build_zonelists(pgdat);

build_zonelist_cache(pgdat);

}

遍歷每一個CPU,初始化他們的per_cpu緩存

for_each_possible_cpu(cpu)?{

setup_pageset(&per_cpu(boot_pageset,?cpu),?0);

......

}

return?0;

}

【start_kernel--->build_all_zonelists--->__build_all_zonelists--->build_zonelists

struct?zonelist是備用結點內存域列表管理結構,該結構在文件include/linux/mmzone.h中定義,如下:

struct?zonelist?{

指針zlcache_ptr通常指向本結構中的zlcache成員

struct?zonelist_cache?*zlcache_ptr;

MAX_ZONES_PER_ZONELIST表示所有節點內存域總和

struct?zoneref?_zonerefs[MAX_ZONES_PER_ZONELIST?+?1];

#ifdef?CONFIG_NUMA

struct?zonelist_cache?zlcache;

#endif

};

struct?zoneref?{

struct?zone?*zone;???指向內存域管理結構

int?zone_idx;????內存域所在結點id

};

struct?zonelist_cache?{

存儲所有內存域對應結點號

unsigned?short?z_to_n[MAX_ZONES_PER_ZONELIST];

fullzones是所有內存域的一個位圖,如果內存域對應位置1,表示這個內存域已沒有可用內存

DECLARE_BITMAP(fullzones,?MAX_ZONES_PER_ZONELIST);

unsigned?long?last_full_zap;

};

備用結點內存域列表中內存域排列原則是:按分配代價由小到大排列。在節點間應當先排列本地結點的內存域后排其他結點內存域,在節點內先排列高端內存、然后是普通內存、再然后是DMA內存。

在結點描述結構中,節點的備用結點內存域列表定義如下:

typedef?struct?pglist_data?{

......

struct?zonelist?node_zonelists[MAX_ZONELISTS];

......

}

在多處理器系統中MAX_ZONELISTS定義為2,node_zonelists[0]中排列本結點內存域,node_zonelists[1]中排列其他備用結點內存域。如果在找node_zonelists[0]中不到可用內存就到node_zonelists[1]中去分配。

mm/page_alloc.c

static?void?build_zonelists(pg_data_t?*pgdat)

{

int?j,?node,?load;

enum?zone_type?i;

nodemask_t?used_mask;

int?local_node,?prev_node;

struct?zonelist?*zonelist;

int?order?=?current_zonelist_order;

初始化備用結點內存域

for?(i?=?0;?i?

zonelist?=?pgdat->node_zonelists?+?i;

zonelist->_zonerefs[0].zone?=?NULL;

zonelist->_zonerefs[0].zone_idx?=?0;

}

local_node?=?pgdat->node_id;

load?=?nr_online_nodes;

prev_node?=?local_node;

nodes_clear(used_mask);

memset(node_order,?0,?sizeof(node_order));

j?=?0;

找一個與結點pgdat距離最近的結點

while?((node?=?find_next_best_node(local_node,?&used_mask))?>=?0)?{

int?distance?=?node_distance(local_node,?node);

if?(distance?>?RECLAIM_DISTANCE)

zone_reclaim_mode?=?1;

if?(distance?!=?node_distance(local_node,?prev_node))

node_load[node]?=?load;

prev_node?=?node;

load--;

if?(order?==?ZONELIST_ORDER_NODE)

將找到的最佳結點內存域排列到pgdat的備用內存域列表node_zonelists[1]中

build_zonelists_in_node_order(pgdat,?node);

else

node_order[j++]?=?node;?/*?remember?order?*/

}

if?(order?==?ZONELIST_ORDER_ZONE)?{

/*?calculate?node?order?--?i.e.,?DMA?last!?*/

build_zonelists_in_zone_order(pgdat,?j);

}

將結點自己的內存域排列到自己的備用結點內存域node_zonelists[0]中

build_thisnode_zonelists(pgdat);

}

【start_kernel--->build_all_zonelists--->__build_all_zonelists--->build_zonelists

--->build_zonelists_in_node_order】

mm/page_alloc.c

static?void?build_zonelists_in_node_order(pg_data_t?*pgdat,?int?node)

{

int?j;

struct?zonelist?*zonelist;

函數build_thisnode_zonelists和本函數最大的區別就在于這里取的是node_zonelists[0],而在函數build_thisnode_zonelists中取的是node_zonelists[1]。

zonelist?=?&pgdat->node_zonelists[0];

找到第一個空的位置

for?(j?=?0;?zonelist->_zonerefs[j].zone?!=?NULL;?j++)

;

將結點node的所有內存區域排列在,j開始的備用列表中

j?=?build_zonelists_node(NODE_DATA(node),?zonelist,?j,

MAX_NR_ZONES?-?1);

zonelist->_zonerefs[j].zone?=?NULL;

zonelist->_zonerefs[j].zone_idx?=?0;

}

【start_kernel--->build_all_zonelists--->__build_all_zonelists--->build_zonelists

--->build_zonelists_in_node_order--->build_zonelists_node】

mm/page_alloc.c

static?int?build_zonelists_node(pg_data_t?*pgdat,?struct?zonelist?*zonelist,

int?nr_zones,?enum?zone_type?zone_type)

{

struct?zone?*zone;

BUG_ON(zone_type?>=?MAX_NR_ZONES);

zone_type++;

do?{

這里的zone_type,上層函數傳入的是MAX_NR_ZONES?,循環中做的就是將內存域按ZONE_HIGHMEM--->ZONE_NORMAL--->ZONE_DMA的順序排列在備用內存列表中

zone_type--;

zone?=?pgdat->node_zones?+?zone_type;

if?(populated_zone(zone))?{

函數zoneref_set_zone要做的就是用zone和zone所在結點id來初始化_zonerefs。

zoneref_set_zone(zone,

&zonelist->_zonerefs[nr_zones++]);

找出整個備用列表中內存區域類型值最大的的內存區域

check_highest_zone(zone_type);

}

}?while?(zone_type);

return?nr_zones;

}

【start_kernel--->build_all_zonelists--->__build_all_zonelists--->build_zonelist_cache】

mm/page_alloc.c

static?void?build_zonelist_cache(pg_data_t?*pgdat)

{

struct?zonelist?*zonelist;

struct?zonelist_cache?*zlc;

struct?zoneref?*z;

zonelist?=?&pgdat->node_zonelists[0];

讓結構struct?zonelist的zlcache_ptr指針成員指向它自己的zlcache成員

zonelist->zlcache_ptr?=?zlc?=?&zonelist->zlcache;

bitmap_zero(zlc->fullzones,?MAX_ZONES_PER_ZONELIST);

for?(z?=?zonelist->_zonerefs;?z->zone;?z++)

zlc->z_to_n[z?-?zonelist->_zonerefs]?=?zonelist_node_idx(z);

}

【start_kernel--->mm_init】

init/main.c

static?void?__init?mm_init(void)

{

為page_cgroup相關結構分配存儲空間

page_cgroup_init_flatmem();

將自舉分配器bootmem中的空閑空間釋放到伙伴系統中,并停用自舉分配器切換到伙伴系統中

mem_init();

啟用slab分配器,后面另起章節專門講解

kmem_cache_init();

一些per_cpu結構在系統啟動期間做了臨時初始化,這里將進行一些更完善的處理

percpu_init_late();

pgtable_cache_init();為空

初始化非連續內存分配。關于非線性內存區域的分配,后面專門有章節來講解

vmalloc_init();

}

【start_kernel--->mm_init--->mem_init】

arch/arm/mm/init.c

void?__init?mem_init(void)

{

unsigned?long?reserved_pages,?free_pages;

struct?memblock_region?*reg;

int?i;

......

系統中可能有多個存儲塊,每個存儲塊之間可能有沒用的區域,將這些?區域在bootmem中標記為空閑

free_unused_memmap(&meminfo);

將bootmem中空閑區域釋放到伙伴系統中去

totalram_pages?+=?free_all_bootmem();

#ifdef?CONFIG_SA1111

PHYS_PFN_OFFSET是物理存儲位置對應的頁幀號。將PHYS_PFN_OFFSET到內核頁表swapper_pg_dir之間的區域釋放到伙伴系統中去

totalram_pages?+=?free_area(PHYS_PFN_OFFSET,

__phys_to_pfn(__pa(swapper_pg_dir)),?NULL);

#endif

將空閑的高端內存區域釋放到伙伴系統中去

free_highpages();

reserved_pages?=?free_pages?=?0;

統計所有內存塊中的空閑頁和非空閑頁。

for_each_bank(i,?&meminfo)?{

struct?membank?*bank?=?&meminfo.bank[i];

unsigned?int?pfn1,?pfn2;

struct?page?*page,?*end;

pfn1?=?bank_pfn_start(bank);

pfn2?=?bank_pfn_end(bank);

page?=?pfn_to_page(pfn1);

end??=?pfn_to_page(pfn2?-?1)?+?1;

do?{

if?(PageReserved(page))

reserved_pages++;

else?if?(!page_count(page))

free_pages++;

page++;

}?while?(page?

}

......

}

【start_kernel--->

總結

以上是生活随笔為你收集整理的linux内存管理分析 二,linux内存管理分析【二】的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。