前面已经分析了linux内存管理算法(伙伴管理算法)的准备工作。

具体的算法初始化则回到start_kernel()函数接着往下走,下一个函数是mm_init():

【file:/init/main.c】
/*
* Set up kernel memory allocators
*/
static void __init mm_init(void)
{
/*
* page_cgroup requires contiguous pages,
* bigger than MAX_ORDER unless SPARSEMEM.
*/
page_cgroup_init_flatmem();
mem_init();
kmem_cache_init();
percpu_init_late();
pgtable_init();
vmalloc_init();
}

乍看仅仅是几个函数的调用,实际上这里的事情远远没这么简单。其中page_cgroup_init_flatmem()与cgroup相关,而mem_init()则是管理伙伴管理算法的初始化,此外kmem_cache_init()是用于内核slub内存分配体系的初始化,而vmalloc_init()则是用于vmalloc的初始化。

当前主要分析伙伴管理算法,则仅对mem_init()做专门的分析,其余的暂且后面再分析。

伙伴管理算法的初始化函数入口是mem_init(),其实现:

【file:/arch/x86/mm/init_32.c】
void __init mem_init(void)
{
pci_iommu_alloc(); #ifdef CONFIG_FLATMEM
BUG_ON(!mem_map);
#endif
/*
* With CONFIG_DEBUG_PAGEALLOC initialization of highmem pages has to
* be done before free_all_bootmem(). Memblock use free low memory for
* temporary data (see find_range_array()) and for this purpose can use
* pages that was already passed to the buddy allocator, hence marked as
* not accessible in the page tables when compiled with
* CONFIG_DEBUG_PAGEALLOC. Otherwise order of initialization is not
* important here.
*/
set_highmem_pages_init(); /* this will put all low memory onto the freelists */
free_all_bootmem(); after_bootmem = 1; mem_init_print_info(NULL);
printk(KERN_INFO "virtual kernel memory layout:\n"
" fixmap : 0x%08lx - 0x%08lx (%4ld kB)\n"
#ifdef CONFIG_HIGHMEM
" pkmap : 0x%08lx - 0x%08lx (%4ld kB)\n"
#endif
" vmalloc : 0x%08lx - 0x%08lx (%4ld MB)\n"
" lowmem : 0x%08lx - 0x%08lx (%4ld MB)\n"
" .init : 0x%08lx - 0x%08lx (%4ld kB)\n"
" .data : 0x%08lx - 0x%08lx (%4ld kB)\n"
" .text : 0x%08lx - 0x%08lx (%4ld kB)\n",
FIXADDR_START, FIXADDR_TOP,
(FIXADDR_TOP - FIXADDR_START) >> 10, #ifdef CONFIG_HIGHMEM
PKMAP_BASE, PKMAP_BASE+LAST_PKMAP*PAGE_SIZE,
(LAST_PKMAP*PAGE_SIZE) >> 10,
#endif VMALLOC_START, VMALLOC_END,
(VMALLOC_END - VMALLOC_START) >> 20, (unsigned long)__va(0), (unsigned long)high_memory,
((unsigned long)high_memory - (unsigned long)__va(0)) >> 20, (unsigned long)&__init_begin, (unsigned long)&__init_end,
((unsigned long)&__init_end -
(unsigned long)&__init_begin) >> 10, (unsigned long)&_etext, (unsigned long)&_edata,
((unsigned long)&_edata - (unsigned long)&_etext) >> 10, (unsigned long)&_text, (unsigned long)&_etext,
((unsigned long)&_etext - (unsigned long)&_text) >> 10); /*
* Check boundaries twice: Some fundamental inconsistencies can
* be detected at build time already.
*/
#define __FIXADDR_TOP (-PAGE_SIZE)
#ifdef CONFIG_HIGHMEM
BUILD_BUG_ON(PKMAP_BASE + LAST_PKMAP*PAGE_SIZE > FIXADDR_START);
BUILD_BUG_ON(VMALLOC_END > PKMAP_BASE);
#endif
#define high_memory (-128UL << 20)
BUILD_BUG_ON(VMALLOC_START >= VMALLOC_END);
#undef high_memory
#undef __FIXADDR_TOP
#ifdef CONFIG_RANDOMIZE_BASE
BUILD_BUG_ON(CONFIG_RANDOMIZE_BASE_MAX_OFFSET > KERNEL_IMAGE_SIZE);
#endif #ifdef CONFIG_HIGHMEM
BUG_ON(PKMAP_BASE + LAST_PKMAP*PAGE_SIZE > FIXADDR_START);
BUG_ON(VMALLOC_END > PKMAP_BASE);
#endif
BUG_ON(VMALLOC_START >= VMALLOC_END);
BUG_ON((unsigned long)high_memory > VMALLOC_START); if (boot_cpu_data.wp_works_ok < 0)
test_wp_bit();
}

其中pci_iommu_alloc()不是伙伴算法重点相关的函数,不过还是稍微记录一下:

【file:/arch/x86/kernel/pci-dma.c】
void __init pci_iommu_alloc(void)
{
struct iommu_table_entry *p; sort_iommu_table(__iommu_table, __iommu_table_end);
check_iommu_entries(__iommu_table, __iommu_table_end); for (p = __iommu_table; p < __iommu_table_end; p++) {
if (p && p->detect && p->detect() > 0) {
p->flags |= IOMMU_DETECTED;
if (p->early_init)
p->early_init();
if (p->flags & IOMMU_FINISH_IF_DETECTED)
break;
}
}
}

该函数主要是将iommu table先行排序检查,然后调用各个表项注册的函数进行初始化。

而接着的set_highmem_pages_init()则是伙伴算法的开始:

【file:/arch/x86/mm/highmem_32.c】
void __init set_highmem_pages_init(void)
{
struct zone *zone;
int nid; /*
* Explicitly reset zone->managed_pages because set_highmem_pages_init()
* is invoked before free_all_bootmem()
*/
reset_all_zones_managed_pages();
for_each_zone(zone) {
unsigned long zone_start_pfn, zone_end_pfn; if (!is_highmem(zone))
continue; zone_start_pfn = zone->zone_start_pfn;
zone_end_pfn = zone_start_pfn + zone->spanned_pages; nid = zone_to_nid(zone);
printk(KERN_INFO "Initializing %s for node %d (%08lx:%08lx)\n",
zone->name, nid, zone_start_pfn, zone_end_pfn); add_highpages_with_active_regions(nid, zone_start_pfn,
zone_end_pfn);
}
}

其中for_each_free_mem_range(i, nid, &start, &end, NULL)用于遍历查找memblock算法中空闲的空间区域,然后通过clamp_t()对空间区域进行去除内存空洞调整。里面的for ( ; pfn < e_pfn; pfn++)则用于将空间区域的各页面通过free_highmem_page()进行释放处理,其中if (pfn_valid(pfn))用于判断页面的有效性,而pfn_to_page(pfn)则是将页框号转换为页面管理结构。

进一步分析free_highmem_page()实现:

【file:/mm/page_alloc.c】
void free_highmem_page(struct page *page)
{
__free_reserved_page(page);
totalram_pages++;
page_zone(page)->managed_pages++;
totalhigh_pages++;
}

其中totalram_pages用于记录内存的总页面数,page_zone(page)->managed_pages则是记录管理区的管理页面数,totalhigh_pages则是记录高端内存的页面总数;

具体看一下__free_reserved_page():

【file:/include/linux/mm.h】
/* Free the reserved page into the buddy system, so it gets managed. */
static inline void __free_reserved_page(struct page *page)
{
ClearPageReserved(page);
init_page_count(page);
__free_page(page);
}

其中ClearPageReserved定义在/include/linux/page-flags.h中:

#define CLEARPAGEFLAG(uname, lname)                 \

static inline void ClearPage##uname(struct page *page)          \

                                    { clear_bit(PG_##lname, &page->flags); }

用于清除页面的flag中的reserved标志位,表示页面属于动态内存。

接着的init_page_count()这是设置页面的_count引用计数,设置为1,用于为__free_page()释放页面到内存管理算法中做准备。最后的__free_page(),该函数既是初始化伙伴管理算法,同时也是伙伴管理算法释放页面的操作函数。暂且搁置分析__free_page()的实现,后面再详细深入。

接着回到mem_init ()里面下一个调用free_all_bootmem():

【file:/mm/nobootmem.c】
unsigned long __init free_all_bootmem(void)
{
unsigned long pages; reset_all_zones_managed_pages(); /*
* We need to use NUMA_NO_NODE instead of NODE_DATA(0)->node_id
* because in some case like Node0 doesn't have RAM installed
* low ram will be on Node1
*/
pages = free_low_memory_core_early();
totalram_pages += pages; return pages;
}

其中reset_all_zones_managed_pages()是用于重置管理区zone结构中的managed_pages成员数据,着重分析一下free_low_memory_core_early()实现:

该函数通过for_each_free_mem_range()遍历memblock算法中的空闲内存空间,并调用__free_memory_core()来释放;而后面的get_allocated_memblock_reserved_regions_info()和get_allocated_memblock_memory_regions_info()用于获取通过申请而得的memblock管理算法空间,然后释放,其中如果其算法管理空间是系统定义的memblock_reserved_init_regions和memblock_memory_init_regions则仍保留不予以释放。

最后着重分析一下__free_memory_core()的实现:

【file:/mm/nobootmem.c】
static void __init __free_pages_memory(unsigned long start, unsigned long end)
{
int order; while (start < end) {
order = min(MAX_ORDER - 1UL, __ffs(start)); while (start + (1UL << order) > end)
order--; __free_pages_bootmem(pfn_to_page(start), order); start += (1UL << order);
}
}

其里面的__free_pages_bootmem()则:

【file:/mm/nobootmem.c】
void __init __free_pages_bootmem(struct page *page, unsigned int order)
{
unsigned int nr_pages = 1 << order;
struct page *p = page;
unsigned int loop; prefetchw(p);
for (loop = 0; loop < (nr_pages - 1); loop++, p++) {
prefetchw(p + 1);
__ClearPageReserved(p);
set_page_count(p, 0);
}
__ClearPageReserved(p);
set_page_count(p, 0); page_zone(page)->managed_pages += nr_pages;
set_page_refcounted(page);
__free_pages(page, order);
}

由此可以看到,其最终调用的还是__free_pages()将页面予以释放。该函数在后面集中进行分析。

至此,伙伴管理算法初始化完毕。

Linux-3.14.12内存管理笔记【伙伴管理算法(2)】的更多相关文章

  1. Linux-3.14.12内存管理笔记【伙伴管理算法(1)】

    前面分析了memblock算法.内核页表的建立.内存管理框架的构建,这些都是x86处理的setup_arch()函数里面初始化的,因地制宜,具有明显处理器的特征.而start_kernel()接下来的 ...

  2. Linux-3.14.12内存管理笔记【伙伴管理算法(4)】

    此处承接前面未深入分析的页面释放部分,主要详细分析伙伴管理算法中页面释放的实现.页面释放的函数入口是__free_page(),其实则是一个宏定义. 具体实现: [file:/include/linu ...

  3. Linux-3.14.12内存管理笔记【伙伴管理算法(3)】

    前面分析了伙伴管理算法的初始化,在切入分析代码实现之前,例行先分析一下其实现原理. 伙伴管理算法(也称之为Buddy算法),该算法将所有空闲的页面分组划分为MAX_ORDER个页面块链表进行管理,其中 ...

  4. Linux-3.14.12内存管理笔记【构建内存管理框架(1)】

    传统的计算机结构中,整个物理内存都是一条线上的,CPU访问整个内存空间所需要的时间都是相同的.这种内存结构被称之为UMA(Uniform Memory Architecture,一致存储结构).但是随 ...

  5. 2. Linux-3.14.12内存管理笔记【系统启动阶段的memblock算法(2)】

    memory:表示可用可分配的内存: 结束完memblock算法初始化前的准备工作,回到memblock算法初始化及其算法实现上面.memblock是一个很简单的算法. memblock算法的实现是, ...

  6. Linux-3.14.12内存管理笔记【kmalloc与kfree实现】【转】

    本文转载自:http://blog.chinaunix.net/uid-26859697-id-5573776.html kmalloc()是基于slab/slob/slub分配分配算法上实现的,不少 ...

  7. Linux-3.14.12内存管理笔记【构建内存管理框架(5)】

    前面已经分析了内存管理框架的构建实现过程,有部分内容未完全呈现出来,这里主要做个补充. 如下图,这是前面已经看到过的linux物理内存管理框架的层次关系. 现着重分析一下各个管理结构体的成员功能作用. ...

  8. Linux-3.14.12内存管理笔记【建立内核页表(1)】

    前面已经分析过了Intel的内存映射和linux的基本使用情况,已知head_32.S仅是建立临时页表,内核还是要建立内核页表,做到全面映射的.下面就基于RAM大于896MB,而小于4GB ,切CON ...

  9. 1. Linux-3.14.12内存管理笔记【系统启动阶段的memblock算法(1)】

    memblock算法是linux内核初始化阶段的一个内存分配器(它取代了原来的bootmem算法),实现较为简单.负责page allocator初始化之前的内存管理和分配请求. 分析memblock ...

随机推荐

  1. C语言笔记 01_介绍&环境设置&编译执行

    前言 我是作为一个前端开发者入的编程世界,经过时间的推移,我发现对于编程底层的一些东西一点都不了解,只拘泥于表面,所以想尝试学习C语言然后进一步了解底层机制. 介绍 C 语言是一种通用的.面向过程式的 ...

  2. 【重学Node.js 第5篇】部署项目到腾讯云服务器

    课程介绍看这里:https://www.cnblogs.com/zhangran/p/11963616.html 项目github地址:https://github.com/hellozhangran ...

  3. Jmeter性能测试分布式技术

    一.什么是分布式测试 分布式测试是指通过局域网和Internet,把分布于不同地点.独立完成特定功能的测试计算机连接起来,以达到测试资源共享.分散操作.集中管理.协同工作.负载均衡.测试过程监控等目的 ...

  4. TensorFlow实现图像卷积并可视化示例

    图片尺寸要自己修改. 看起来好像没啥意思,不知道下一步能干什么,先卷了再说.由于weights是随机生成的(tf.random_normal作用:用于从服从指定正太分布的数值中取出随机数),所以每次卷 ...

  5. LATEX Mathematical Symbols

    原文地址:https://www.caam.rice.edu/~heinken/latex/symbols.pdf

  6. Python基础语法-List

    列表的操作方法 列表中存放的数据是可以进行修改的,比如"增"."删"."改"" 添加元素("增" append ...

  7. MySQL数据库:聚合函数的使用

    聚合函数 max() 最大值 min() 最小值 avg() 平均值 sum() 求和 count() 符合条件数据的数目 聚合函数不能嵌套使用 # 在统计时字段内没有满足条件的数值只有count返回 ...

  8. js中自执行函数(function(){})()和(function(){}())区别

    方式一,调用函数,得到返回值.强制函数直接量执行再返回一个引用,引用在去调用执行方式二,调用函数,得到返回值.强制运算符使函数调用执行(function(){})(); 是 把函数当作表达式解析,然后 ...

  9. 进制转换器V1.0_Beta

    一.截图部分 二.代码部分: char2num() 作用:将字符转化成对应的数字        e.g.   '9'->9    'A'->10 int char2num(char ch) ...

  10. 高阶函数&&高阶组件(二)

    高阶组件总共分为两大类 代理方式 操纵prop 访问ref(不推荐) 抽取状态 包装组件 继承方式 操纵生命周期 操纵prop 代理方式之 操纵prop 删除prop import React fro ...