Kernel 3.0.8 内存管理函数【转】

转自:http://blog.csdn.net/myarrow/article/details/7208777

1. 内存分配函数

相关代码如下:

#define alloc_pages(gfp_mask, order)   alloc_pages_node(numa_node_id(), gfp_mask, order)
#define alloc_page_vma(gfp_mask, vma, addr) alloc_pages(gfp_mask, 0)
#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)

#define __get_free_page(gfp_mask)   __get_free_pages((gfp_mask),0)
#define __get_dma_pages(gfp_mask, order)   __get_free_pages((gfp_mask) | GFP_DMA,(order))

#define pfn_to_page(pfn) (mem_map + ((pfn) - PHYS_PFN_OFFSET))
#define page_to_pfn(page) ((unsigned long)((page) - mem_map) + PHYS_PFN_OFFSET)
#define pfn_valid(pfn) ((pfn) >= PHYS_PFN_OFFSET && (pfn) < (PHYS_PFN_OFFSET + max_mapnr))

#define phys_to_page(phys) (pfn_to_page(phys >> PAGE_SHIFT))
#define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT)

#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)

1)__get_free_pages实现代码如下,它返回页的虚拟地址:

[cpp] view plaincopy

  1. unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
  2. {
  3. struct page *page;
  4. /*
  5. * __get_free_pages() returns a 32-bit address, which cannot represent
  6. * a highmem page
  7. */
  8. VM_BUG_ON((gfp_mask & __GFP_HIGHMEM) != 0);
  9. page = alloc_pages(gfp_mask, order);
  10. if (!page)
  11. return 0;
  12. return (unsigned long) page_address(page);
  13. }

[cpp] view plaincopy

  1. /**
  2. * page_address - get the mapped virtual address of a page
  3. * @page: &struct page to get the virtual address of
  4. *
  5. * Returns the page‘s virtual address.
  6. */
  7. void *page_address(struct page *page)
  8. {
  9. unsigned long flags;
  10. void *ret;
  11. struct page_address_slot *pas;
  12. if (!PageHighMem(page))
  13. return lowmem_page_address(page);
  14. pas = page_slot(page);
  15. ret = NULL;
  16. spin_lock_irqsave(&pas->lock, flags);
  17. if (!list_empty(&pas->lh)) {
  18. struct page_address_map *pam;
  19. list_for_each_entry(pam, &pas->lh, list) {
  20. if (pam->page == page) {
  21. ret = pam->virtual;
  22. goto done;
  23. }
  24. }
  25. }
  26. done:
  27. spin_unlock_irqrestore(&pas->lock, flags);
  28. return ret;
  29. }

[cpp] view plaincopy

  1. static __always_inline void *lowmem_page_address(struct page *page)
  2. {
  3. return __va(PFN_PHYS(page_to_pfn(page)));
  4. }

2)alloc_pages_node

[cpp] view plaincopy

  1. static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,
  2. unsigned int order)
  3. {
  4. /* Unknown node is current node */
  5. if (nid < 0)
  6. nid = numa_node_id();
  7. return __alloc_pages(gfp_mask, order, node_zonelist(nid, gfp_mask));
  8. }

参数nid是要分配内存的 NUMA节点 ID,
参数gfp_mask是 GFP_分配标志,
参数order是分配内存的大小(2^order个页面).
返回值是一个指向第一个(可能返回多个页)page结构的指针,失败时返回NULL。

[cpp] view plaincopy

  1. static inline struct page *
  2. __alloc_pages(gfp_t gfp_mask, unsigned int order,
  3. struct zonelist *zonelist)
  4. {
  5. return __alloc_pages_nodemask(gfp_mask, order, zonelist, NULL);
  6. }

[cpp] view plaincopy

  1. /*
  2. * This is the ‘heart‘ of the zoned buddy allocator.
  3. */
  4. struct page *
  5. __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
  6. struct zonelist *zonelist, nodemask_t *nodemask)
  7. {
  8. enum zone_type high_zoneidx = gfp_zone(gfp_mask);
  9. struct zone *preferred_zone;
  10. struct page *page;
  11. int migratetype = allocflags_to_migratetype(gfp_mask);
  12. gfp_mask &= gfp_allowed_mask;
  13. lockdep_trace_alloc(gfp_mask);
  14. might_sleep_if(gfp_mask & __GFP_WAIT);
  15. if (should_fail_alloc_page(gfp_mask, order))
  16. return NULL;
  17. /*
  18. * Check the zones suitable for the gfp_mask contain at least one
  19. * valid zone. It‘s possible to have an empty zonelist as a result
  20. * of GFP_THISNODE and a memoryless node
  21. */
  22. if (unlikely(!zonelist->_zonerefs->zone))
  23. return NULL;
  24. get_mems_allowed();
  25. /* The preferred zone is used for statistics later */
  26. first_zones_zonelist(zonelist, high_zoneidx,
  27. nodemask ? : &cpuset_current_mems_allowed,
  28. &preferred_zone);
  29. if (!preferred_zone) {
  30. put_mems_allowed();
  31. return NULL;
  32. }
  33. /* First allocation attempt */
  34. page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, nodemask, order,
  35. zonelist, high_zoneidx, ALLOC_WMARK_LOW|ALLOC_CPUSET,
  36. preferred_zone, migratetype);
  37. if (unlikely(!page))
  38. page = __alloc_pages_slowpath(gfp_mask, order,
  39. zonelist, high_zoneidx, nodemask,
  40. preferred_zone, migratetype);
  41. put_mems_allowed();
  42. trace_mm_page_alloc(page, order, gfp_mask, migratetype);
  43. return page;
  44. }

其接下来的主要调用流程如下:

get_page_from_freelist->

buffered_rmqueue

3) buffered_rmqueue

从区域zone中获取一块大小为2^order的物理内存块,返回该内存块的首个页框的描述符page。

[cpp] view plaincopy

  1. static inline
  2. struct page *buffered_rmqueue(struct zone *preferred_zone,
  3. struct zone *zone, int order, gfp_t gfp_flags,
  4. int migratetype)
  5. {
  6. unsigned long flags;
  7. struct page *page;
  8. int cold = !!(gfp_flags & __GFP_COLD);
  9. again:
  10. if (likely(order == 0)) { //获取一页物理内存(2^0),从当前cpu的高速缓存内存中申请
  11. struct per_cpu_pages *pcp;
  12. struct list_head *list;
  13. local_irq_save(flags);
  14. pcp = &this_cpu_ptr(zone->pageset)->pcp; //获取zone的当前处理器的高速缓存内存描述结构指针
  15. list = &pcp->lists[migratetype];
  16. if (list_empty(list)) { //高速缓存内存为空
  17. pcp->count += rmqueue_bulk(zone, 0,//调用此函数从伙伴系统中分配batch空闲内存到高速缓存内存中
  18. pcp->batch, list,
  19. migratetype, cold);
  20. if (unlikely(list_empty(list)))
  21. goto failed;
  22. }
  23. //我们从pcp->list链表开始的第一个lru起,去寻找相应的struct page结构体
  24. if (cold)
  25. page = list_entry(list->prev, struct page, lru);
  26. else
  27. page = list_entry(list->next, struct page, lru);
  28. //由于被分配出去了,所以高速缓存内存中不再包含这页内存,所以从链表里删除这一项。
  29. list_del(&page->lru);
  30. pcp->count--;  //相应的当前页数也要减少
  31. } else { //获取一块物理内存(2^order)
  32. if (unlikely(gfp_flags & __GFP_NOFAIL)) {
  33. /*
  34. * __GFP_NOFAIL is not to be used in new code.
  35. *
  36. * All __GFP_NOFAIL callers should be fixed so that they
  37. * properly detect and handle allocation failures.
  38. *
  39. * We most definitely don‘t want callers attempting to
  40. * allocate greater than order-1 page units with
  41. * __GFP_NOFAIL.
  42. */
  43. WARN_ON_ONCE(order > 1);
  44. }
  45. spin_lock_irqsave(&zone->lock, flags);
  46. page = __rmqueue(zone, order, migratetype); //调用函数申请内存
  47. spin_unlock(&zone->lock);
  48. if (!page)
  49. goto failed;
  50. __mod_zone_page_state(zone, NR_FREE_PAGES, -(1 << order));
  51. }
  52. __count_zone_vm_events(PGALLOC, zone, 1 << order);
  53. zone_statistics(preferred_zone, zone, gfp_flags);
  54. local_irq_restore(flags);
  55. VM_BUG_ON(bad_range(zone, page));
  56. if (prep_new_page(page, order, gfp_flags))
  57. goto again;
  58. return page; //返回申请到的内存空间的首页内存页的struct page结构指针
  59. failed:
  60. local_irq_restore(flags);
  61. return NULL;
  62. }

4) rmqueue_bulk

用于多次(count)内存申请.

[cpp] view plaincopy

  1. /*
  2. * Obtain a specified number of elements from the buddy allocator, all under
  3. * a single hold of the lock, for efficiency.  Add them to the supplied list.
  4. * Returns the number of new pages which were placed at *list.
  5. */
  6. static int rmqueue_bulk(struct zone *zone, unsigned int order,
  7. unsigned long count, struct list_head *list,
  8. int migratetype, int cold)
  9. {
  10. int i;
  11. spin_lock(&zone->lock);
  12. for (i = 0; i < count; ++i) {
  13. struct page *page = __rmqueue(zone, order, migratetype);
  14. if (unlikely(page == NULL))
  15. break;
  16. /*
  17. * Split buddy pages returned by expand() are received here
  18. * in physical page order. The page is added to the callers and
  19. * list and the list head then moves forward. From the callers
  20. * perspective, the linked list is ordered by page number in
  21. * some conditions. This is useful for IO devices that can
  22. * merge IO requests if the physical pages are ordered
  23. * properly.
  24. */
  25. if (likely(cold == 0))
  26. list_add(&page->lru, list);
  27. else
  28. list_add_tail(&page->lru, list);
  29. set_page_private(page, migratetype);
  30. list = &page->lru;
  31. }
  32. __mod_zone_page_state(zone, NR_FREE_PAGES, -(i << order));
  33. spin_unlock(&zone->lock);
  34. return i;
  35. }

5) __rmqueue

用于一次内存申请。

[cpp] view plaincopy

  1. /*
  2. * Do the hard work of removing an element from the buddy allocator.
  3. * Call me with the zone->lock already held.
  4. */
  5. static struct page *__rmqueue(struct zone *zone, unsigned int order,
  6. int migratetype)
  7. {
  8. struct page *page;
  9. retry_reserve:
  10. page = __rmqueue_smallest(zone, order, migratetype);
  11. if (unlikely(!page) && migratetype != MIGRATE_RESERVE) {
  12. page = __rmqueue_fallback(zone, order, migratetype);
  13. /*
  14. * Use MIGRATE_RESERVE rather than fail an allocation. goto
  15. * is used because __rmqueue_smallest is an inline function
  16. * and we want just one call site
  17. */
  18. if (!page) {
  19. migratetype = MIGRATE_RESERVE;
  20. goto retry_reserve;
  21. }
  22. }
  23. trace_mm_page_alloc_zone_locked(page, order, migratetype);
  24. return page;
  25. }

2. 内存释放函数

相关宏定义如下:

[cpp] view plaincopy

    1. #define __free_page(page) __free_pages((page), 0)
    2. #define free_page(addr) free_pages((addr),0)
时间: 2024-08-07 00:06:39

Kernel 3.0.8 内存管理函数【转】的相关文章

linux kernel学习笔记-5内存管理(转)

http://blog.sina.com.cn/s/blog_65373f1401019dtz.htmllinux kernel学习笔记-5 内存管理1. 相关的数据结构 相比用户空间而言,在内核中分配内存往往受到更多的限制,比如内核中很多情况下不能睡眠,此外处理内存分配失败也不像用户空间那么容易.内核使用了页和区两种数据结构来管理内存: 1.1 页 内核把物理页作为内存管理的基本单位.尽管CPU的最小可寻址单位通常为字(甚至字节),但是MMU(内存管理单元,管理内存并把虚拟地址转换为物理地址的

使用内存管理函数实现动态数组

C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间释放,为有效地使用内存资源提供了手段. 动态数组,指的就是利用内存的申请和释放函数,在程序的运行过程中,根据实际需要指定数组的大小.其本质就是一个指向数组的指针变量. 主要用到的内存管理函数是:malloc和free. 1.分配内存函数malloc: 调用形式:(类型说明符*)malloc(size): 功     能:在内存的动态存储区中分配一块长度为size字节的连续区域. 返     回:该区域

Kernel那些事儿之内存管理(3) --- 久别重逢

上次我们讲到page frame是物理内存的基本组成单位.那Kernel就必须要有一套机制来管理空闲的page frames.这一点不难理解.每个县长必须要把本县可用的劳动力登记在册,这样哪天皇帝要征兵了,你才不至于手忙脚乱. 这个问题看似简单,实则不然.因为这里面有一个外碎片的问题. 在物理内存中,连续的物理内存页有时是很重要的.例如在DMA操作中,由于大部分DMA处理器都没有分页机制,它们会直接访问物理内存地址,因此DMA 所用的缓冲区在物理地址空间必须连续:再例如,使用连续的物理内存页,可

Kernel那些事儿之内存管理(8) --- Slab(中)

上篇讲了Slab中的数据结构,这篇该讲Slab中的操作了. 既然是内存管理,那操作无非就两点:allocate 和 free. 1. 申请一个object 在Slab中,申请一个object是通过函数 kmem_cache_alloc() 来完成的. 3618 void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) 3619 { 3620     return __cache_alloc(cachep, flags, __bu

Kernel那些事儿之内存管理(1)

有人的地方就有江湖.要介绍内存管理这个江湖,首先还得从这里面的主要人物讲起. 在NUMA结构中,物理内存首先被分成若干nodes.每一个node进一步被分成若干zones.每一个zone又关联了一个描述page frames的数组,该数组包含了属于该zone的所有page frame的描述符. 不难看出,在这个江湖里主要有三位重要人物:nodes, zones 和 page frames.这三者的关系和地位大体可以用下图来描述(该图取自"Professional Linux Kernel Arc

Kernel那些事儿之内存管理(6) --- 衣带渐宽终不悔(下)

接着上篇写,继续介绍zone allocator.上一篇介绍了周边,现在来看看它的全貌 --- 函数__alloc_pages(). Kernel源代码里是这样注释函数__alloc_pages()的.其重要地位可见一斑. 1451 /* 1452  * This is the 'heart' of the zoned buddy allocator. 1453  */ __alloc_pages()的工作模式很清晰:利用函数get_page_from_freelist()多次遍历zonelis

Linux C 堆内存管理函数malloc(),calloc(),realloc(),free()详解

C 编程中,经常需要操作的内存可分为下面几个类别: 堆栈区(stack):由编译器自动分配与释放,存放函数的参数值,局部变量,临时变量等等,它们获取的方式都是由编译器自动执行的 堆区(heap):一般由程序员分配与释放,基程序员不释放,程序结束时可能由操作系统回收(C/C++没有此等回收机制,Java/C#有),注意它与数据结构中的堆是两回事,分配方式倒是类似于链表. 全局区(静态区)(static):全局变量和静态变量的存储是放在一块儿的,初始化的全局变量和静态变量在一块区域,未初始化的全局变

内存管理函数

1 calloc函数 [函数原型]:void* calloc (size_t num, size_t size); [功能]: 在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0: [返回值]: 分配成功返回指向该内存的地址,失败则返回 NULL: [头文件]: #include <stdlib.h> 如果 size 的值为 0,那么返回值会因标准库实现的不同而不同,可能是 NULL,也可能不是,但返回的指针不应该再次被引用. 注意:函数的返回值类型是 v

Kernel那些事儿之内存管理(13) --- 内核映射(下)

前面讲过,针对于内核地址空间中后面的128MB空间,Kernel提供了三种机制来映射物理内存.之前讲过了两种,即持久内核映射和临时内核映射.这两种机制的目的都是一样的:使Kernel能够访问到高端内存. 今天讲一下第三种机制:非连续内存分配,也就是vmalloc.这个机制同样可以使Kernel能够访问到高端内存,不过这不是该机制的主要目的.该机制的主要目的是:把物理上不连续的页面映射到连续的内核线性地址空间中. 非连续内存区域管理 既然是映射,肯定会涉及到三个元素:集合L,集合P,映射M. 集合