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

具体实现:

  1. file:/include/linux/gfp.h
  2. #define __free_page(page) __free_pages((page), 0)

而__free_pages()的实现:

  1. file:/mm/page_alloc.c
  2. void __free_pages(struct page *page, unsigned int order)
  3. {
  4. if (put_page_testzero(page)) {
  5. if (order == 0)
  6. free_hot_cold_page(page, 0);
  7. else
  8. __free_pages_ok(page, order);
  9. }
  10. }

其中put_page_testzero()是对page结构的_count引用计数做原子减及测试,用于检查内存页面是否仍被使用,如果不再使用,则进行释放。其中order表示页面数量,如果释放的是单页,则会调用free_hot_cold_page()将页面释放至per-cpu page缓存中,而不是伙伴管理算法;真正的释放至伙伴管理算法的是__free_pages_ok(),同时也是用于多个页面释放的情况。

此处接着则由free_hot_cold_page()开始分析:

  1. file:/mm/page_alloc.c
  2. /*
  3. * Free a 0-order page
  4. * cold == 1 ? free a cold page : free a hot page
  5. */
  6. void free_hot_cold_page(struct page *page, int cold)
  7. {
  8. struct zone *zone = page_zone(page);
  9. struct per_cpu_pages *pcp;
  10. unsigned long flags;
  11. int migratetype;
  12. if (!free_pages_prepare(page, 0))
  13. return;
  14. migratetype = get_pageblock_migratetype(page);
  15. set_freepage_migratetype(page, migratetype);
  16. local_irq_save(flags);
  17. __count_vm_event(PGFREE);
  18. /*
  19. * We only track unmovable, reclaimable and movable on pcp lists.
  20. * Free ISOLATE pages back to the allocator because they are being
  21. * offlined but treat RESERVE as movable pages so we can get those
  22. * areas back if necessary. Otherwise, we may have to free
  23. * excessively into the page allocator
  24. */
  25. if (migratetype >= MIGRATE_PCPTYPES) {
  26. if (unlikely(is_migrate_isolate(migratetype))) {
  27. free_one_page(zone, page, 0, migratetype);
  28. goto out;
  29. }
  30. migratetype = MIGRATE_MOVABLE;
  31. }
  32. pcp = &this_cpu_ptr(zone->pageset)->pcp;
  33. if (cold)
  34. list_add_tail(&page->lru, &pcp->lists[migratetype]);
  35. else
  36. list_add(&page->lru, &pcp->lists[migratetype]);
  37. pcp->count++;
  38. if (pcp->count >= pcp->high) {
  39. unsigned long batch = ACCESS_ONCE(pcp->batch);
  40. free_pcppages_bulk(zone, batch, pcp);
  41. pcp->count -= batch;
  42. }
  43. out:
  44. local_irq_restore(flags);
  45. }

先看一下free_pages_prepare()的实现:

  1. file:/mm/page_alloc.c
  2. static bool free_pages_prepare(struct page *page, unsigned int order)
  3. {
  4. int i;
  5. int bad = 0;
  6. trace_mm_page_free(page, order);
  7. kmemcheck_free_shadow(page, order);
  8. if (PageAnon(page))
  9. page->mapping = NULL;
  10. for (i = 0; i < (1 << order); i++)
  11. bad += free_pages_check(page + i);
  12. if (bad)
  13. return false;
  14. if (!PageHighMem(page)) {
  15. debug_check_no_locks_freed(page_address(page),
  16. PAGE_SIZE << order);
  17. debug_check_no_obj_freed(page_address(page),
  18. PAGE_SIZE << order);
  19. }
  20. arch_free_page(page, order);
  21. kernel_map_pages(page, 1 << order, 0);
  22. return true;
  23. }

其中trace_mm_page_free()用于trace追踪机制;而kmemcheck_free_shadow()用于内存检测工具kmemcheck,如果未定义CONFIG_KMEMCHECK的情况下,它是一个空函数。接着后面的PageAnon()等都是用于检查页面状态的情况,以判断页面是否允许释放,避免错误释放页面。由此可知该函数主要作用是检查和调试。

接着回到free_hot_cold_page()函数中,get_pageblock_migratetype()和set_freepage_migratetype()分别是获取和设置页面的迁移类型,即设置到page->index;local_irq_save()和末尾的local_irq_restore()则用于保存恢复中断请求标识。

  1. if (migratetype >= MIGRATE_PCPTYPES) {
  2. if (unlikely(is_migrate_isolate(migratetype))) {
  3. free_one_page(zone, page, 0, migratetype);
  4. goto out;
  5. }
  6. migratetype = MIGRATE_MOVABLE;
  7. }

这里面的MIGRATE_PCPTYPES用来表示每CPU页框高速缓存的数据结构中的链表的迁移类型数目,如果某个页面类型大于MIGRATE_PCPTYPES则表示其可挂到可移动列表中,如果迁移类型是MIGRATE_ISOLATE则直接将该其释放到伙伴管理算法中。

末尾部分:

  1. pcp = &this_cpu_ptr(zone->pageset)->pcp;
  2. if (cold)
  3. list_add_tail(&page->lru, &pcp->lists[migratetype]);
  4. else
  5. list_add(&page->lru, &pcp->lists[migratetype]);
  6. pcp->count++;
  7. if (pcp->count >= pcp->high) {
  8. unsigned long batch = ACCESS_ONCE(pcp->batch);
  9. free_pcppages_bulk(zone, batch, pcp);
  10. pcp->count -= batch;
  11. }

其中pcp表示内存管理区的每CPU管理结构,cold表示冷热页面,如果是冷页就将其挂接到对应迁移类型的链表尾,而若是热页则挂接到对应迁移类型的链表头。其中if (pcp->count >= pcp->high)判断值得注意,其用于如果释放的页面超过了每CPU缓存的最大页面数时,则将其批量释放至伙伴管理算法中,其中批量数为pcp->batch。

具体分析一下释放至伙伴管理算法的实现free_pcppages_bulk():

  1. file:/mm/page_alloc.c
  2. /*
  3. * Frees a number of pages from the PCP lists
  4. * Assumes all pages on list are in same zone, and of same order.
  5. * count is the number of pages to free.
  6. *
  7. * If the zone was previously in an "all pages pinned" state then look to
  8. * see if this freeing clears that state.
  9. *
  10. * And clear the zone's pages_scanned counter, to hold off the "all pages are
  11. * pinned" detection logic.
  12. */
  13. static void free_pcppages_bulk(struct zone *zone, int count,
  14. struct per_cpu_pages *pcp)
  15. {
  16. int migratetype = 0;
  17. int batch_free = 0;
  18. int to_free = count;
  19. spin_lock(&zone->lock);
  20. zone->pages_scanned = 0;
  21. while (to_free) {
  22. struct page *page;
  23. struct list_head *list;
  24. /*
  25. * Remove pages from lists in a round-robin fashion. A
  26. * batch_free count is maintained that is incremented when an
  27. * empty list is encountered. This is so more pages are freed
  28. * off fuller lists instead of spinning excessively around empty
  29. * lists
  30. */
  31. do {
  32. batch_free++;
  33. if (++migratetype == MIGRATE_PCPTYPES)
  34. migratetype = 0;
  35. list = &pcp->lists[migratetype];
  36. } while (list_empty(list));
  37. /* This is the only non-empty list. Free them all. */
  38. if (batch_free == MIGRATE_PCPTYPES)
  39. batch_free = to_free;
  40. do {
  41. int mt; /* migratetype of the to-be-freed page */
  42. page = list_entry(list->prev, struct page, lru);
  43. /* must delete as __free_one_page list manipulates */
  44. list_del(&page->lru);
  45. mt = get_freepage_migratetype(page);
  46. /* MIGRATE_MOVABLE list may include MIGRATE_RESERVEs */
  47. __free_one_page(page, zone, 0, mt);
  48. trace_mm_page_pcpu_drain(page, 0, mt);
  49. if (likely(!is_migrate_isolate_page(page))) {
  50. __mod_zone_page_state(zone, NR_FREE_PAGES, 1);
  51. if (is_migrate_cma(mt))
  52. __mod_zone_page_state(zone, NR_FREE_CMA_PAGES, 1);
  53. }
  54. } while (--to_free && --batch_free && !list_empty(list));
  55. }
  56. spin_unlock(&zone->lock);
  57. }

里面while大循环用于计数释放指定批量数的页面。其中释放方式是先自MIGRATE_UNMOVABLE迁移类型起(止于MIGRATE_PCPTYPES迁移类型),遍历各个链表统计其链表中页面数:

  1. do {
  2. batch_free++;
  3. if (++migratetype == MIGRATE_PCPTYPES)
  4. migratetype = 0;
  5. list = &pcp->lists[migratetype];
  6. } while (list_empty(list));

如果只有MIGRATE_PCPTYPES迁移类型的链表为非空链表,则全部页面将从该链表中释放。

后面的do{}while()里面,其先将页面从lru链表中去除,然后获取页面的迁移类型,通过__free_one_page()释放页面,最后使用__mod_zone_page_state()修改管理区的状态值。

着重分析一下__free_one_page()的实现:

  1. file:/mm/page_alloc.c
  2. /*
  3. * Freeing function for a buddy system allocator.
  4. *
  5. * The concept of a buddy system is to maintain direct-mapped table
  6. * (containing bit values) for memory blocks of various "orders".
  7. * The bottom level table contains the map for the smallest allocatable
  8. * units of memory (here, pages), and each level above it describes
  9. * pairs of units from the levels below, hence, "buddies".
  10. * At a high level, all that happens here is marking the table entry
  11. * at the bottom level available, and propagating the changes upward
  12. * as necessary, plus some accounting needed to play nicely with other
  13. * parts of the VM system.
  14. * At each level, we keep a list of pages, which are heads of continuous
  15. * free pages of length of (1 << order) and marked with _mapcount
  16. * PAGE_BUDDY_MAPCOUNT_VALUE. Page's order is recorded in page_private(page)
  17. * field.
  18. * So when we are allocating or freeing one, we can derive the state of the
  19. * other. That is, if we allocate a small block, and both were
  20. * free, the remainder of the region must be split into blocks.
  21. * If a block is freed, and its buddy is also free, then this
  22. * triggers coalescing into a block of larger size.
  23. *
  24. * -- nyc
  25. */
  26. static inline void __free_one_page(struct page *page,
  27. struct zone *zone, unsigned int order,
  28. int migratetype)
  29. {
  30. unsigned long page_idx;
  31. unsigned long combined_idx;
  32. unsigned long uninitialized_var(buddy_idx);
  33. struct page *buddy;
  34. VM_BUG_ON(!zone_is_initialized(zone));
  35. if (unlikely(PageCompound(page)))
  36. if (unlikely(destroy_compound_page(page, order)))
  37. return;
  38. VM_BUG_ON(migratetype == -1);
  39. page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);
  40. VM_BUG_ON_PAGE(page_idx & ((1 << order) - 1), page);
  41. VM_BUG_ON_PAGE(bad_range(zone, page), page);
  42. while (order < MAX_ORDER-1) {
  43. buddy_idx = __find_buddy_index(page_idx, order);
  44. buddy = page + (buddy_idx - page_idx);
  45. if (!page_is_buddy(page, buddy, order))
  46. break;
  47. /*
  48. * Our buddy is free or it is CONFIG_DEBUG_PAGEALLOC guard page,
  49. * merge with it and move up one order.
  50. */
  51. if (page_is_guard(buddy)) {
  52. clear_page_guard_flag(buddy);
  53. set_page_private(page, 0);
  54. __mod_zone_freepage_state(zone, 1 << order,
  55. migratetype);
  56. } else {
  57. list_del(&buddy->lru);
  58. zone->free_area[order].nr_free--;
  59. rmv_page_order(buddy);
  60. }
  61. combined_idx = buddy_idx & page_idx;
  62. page = page + (combined_idx - page_idx);
  63. page_idx = combined_idx;
  64. order++;
  65. }
  66. set_page_order(page, order);
  67. /*
  68. * If this is not the largest possible page, check if the buddy
  69. * of the next-highest order is free. If it is, it's possible
  70. * that pages are being freed that will coalesce soon. In case,
  71. * that is happening, add the free page to the tail of the list
  72. * so it's less likely to be used soon and more likely to be merged
  73. * as a higher order page
  74. */
  75. if ((order < MAX_ORDER-2) && pfn_valid_within(page_to_pfn(buddy))) {
  76. struct page *higher_page, *higher_buddy;
  77. combined_idx = buddy_idx & page_idx;
  78. higher_page = page + (combined_idx - page_idx);
  79. buddy_idx = __find_buddy_index(combined_idx, order + 1);
  80. higher_buddy = higher_page + (buddy_idx - combined_idx);
  81. if (page_is_buddy(higher_page, higher_buddy, order + 1)) {
  82. list_add_tail(&page->lru,
  83. &zone->free_area[order].free_list[migratetype]);
  84. goto out;
  85. }
  86. }
  87. list_add(&page->lru, &zone->free_area[order].free_list[migratetype]);
  88. out:
  89. zone->free_area[order].nr_free++;
  90. }

于while (order < MAX_ORDER-1)前面主要是对释放的页面进行检查校验操作。而while循环内,通过__find_buddy_index()获取与当前释放的页面处于同一阶的伙伴页面索引值,同时藉此索引值计算出伙伴页面地址,并做伙伴页面检查以确定其是否可以合并,若否则退出;接着if (page_is_guard(buddy))用于对页面的debug_flags成员做检查,由于未配置CONFIG_DEBUG_PAGEALLOC,page_is_guard()固定返回false;则剩下的操作主要就是将页面从分配链中摘除,同时将页面合并并将其处于的阶提升一级。

退出while循环后,通过set_page_order()设置页面最终可合并成为的管理阶。最后判断当前合并的页面是否为最大阶,否则将页面放至伙伴管理链表的末尾,避免其过早被分配,得以机会进一步与高阶页面进行合并。末了,将最后的挂入的阶的空闲计数加1。

至此伙伴管理算法的页面释放完毕。

而__free_pages_ok()的页面释放实现调用栈则是:

  1. __free_pages_ok()
  2. —>free_one_page()
  3. —>__free_one_page()

殊途同归,最终还是__free_one_page()来释放,具体的过程就不再仔细分析了。

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

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

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

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

    前面已经分析了linux内存管理算法(伙伴管理算法)的准备工作. 具体的算法初始化则回到start_kernel()函数接着往下走,下一个函数是mm_init(): [file:/init/main. ...

  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. MySql计算字段的长度

    用户账号有用中文字符,查找所有含有中文字符的账号 SELECT member_name FROM table_member WHERE length(member_name)!=char_length ...

  2. 腾讯视频缓存 tdl 转 mp4

    找到腾讯视频->设置,看下缓存文件的目录地址,然后cmd,通过命令进行转化.  copy/b *.tdl 1.mp4

  3. SwiftyUserDefaults-封装系统本地化的框架推荐

    // // ViewController.swift // Test4SwiftyUserDefaults // Copyright © 2017年. All rights reserved. // ...

  4. 使用SSH服务远程管理主机(使用密钥)

    使用ssh服务远程管理主机 SSH是建立在应用层和传输层基础上的安全协议,目的是要在非安全网络上提供安全的远程登录和其他安全网络服务. 通过SSH连接可以远程管理Linux等设备,默认linuxssh ...

  5. VIM操作记录

    =============================================== 2019/12/12_第1次修改 vr7jj ============================= ...

  6. NRF52833蓝牙5.1可用于105℃环境温度的Nordic蓝牙5.1 SoC能实现更广泛的并发多协议低功耗蓝牙、mesh和Thread应用

    Nordic Semiconductor宣布推出nRF52833先进多协议系统级芯片(SoC),这是其广受欢迎且验证通过的nRF52系列的第五个新成员.nRF52833是一款功耗超低的低功耗蓝牙 (B ...

  7. 移动开发在路上-- IOS移动开发系列 网络交互四(1)

    最近一段时间上班忙的没日没夜的,不是披星戴月,就是头天早上出门,第二天早上回家...一直没出处时间来更新博客,码农之苦,说了都是泪,废话不多说,直接本主题,经过之前三篇的讲述,ios开发的东西大家或多 ...

  8. Ubuntu18.04安装openCV4.1.2

    Ubuntu18.04下安装openCV4.1.2 注意: 在整个安装过程中,包括后期QT的使用中不要出现中文路径.会很坑的. 一.下载openCV4.1.2 可以再官网进行下载https://ope ...

  9. CSU OJ1960

    有一棵由N个结点构成的树,每一条边上都有其对应的权值.现在给定起点,求从该点出发的一条路径(至少有一条边)使得这条路径上的权值之和最大,并输出这个最大值. Input 第一行一个正整数T,代表数据组数 ...

  10. Vue底层实现原理总结

    要实现MVVM 响应式原理,要实现如下几点 1.实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者 2.实现一个指令解析器Compile,对每个元素 ...