Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1042693
  • 博文数量: 277
  • 博客积分: 8313
  • 博客等级: 中将
  • 技术积分: 2976
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-22 11:25
文章分类

全部博文(277)

文章存档

2013年(17)

2012年(66)

2011年(104)

2010年(90)

我的朋友

分类: LINUX

2012-05-16 10:31:01

前面总结了非连续内存区域的内核描述,接着看看他的分配和释放。

一、非连续内存区的分配

不管是vmalloc()还是vmalloc_32()等系列的分配函数最后都会调用__vmalloc_node()函数实现,直接看这个函数的实现。

[cpp] view plaincopyprint?

1.         *  __vmalloc_node  -  allocate virtually contiguous memory  

2.         *  @size:      allocation size  

3.         *  @align:     desired alignment  

4.         *  @gfp_mask:  flags for the page level allocator  

5.         *  @prot:      protection mask for the allocated pages  

6.         *  @node:      node to use for allocation or -1  

7.         *  @caller:    caller's return address  

8.         *  

9.         *  Allocate enough pages to cover @size from the page level  

10.       *  allocator with @gfp_mask flags.  Map them into contiguous  

11.       *  kernel virtual space, using a pagetable protection of @prot.  

12.       */  

13.      static void *__vmalloc_node(unsigned long size, unsigned long align,  

14.                      gfp_t gfp_mask, pgprot_t prot,  

15.                      int node, void *caller)  

16.      {  

17.          struct vm_struct *area;  

18.          void *addr;  

19.          unsigned long real_size = size;  

20.        

21.          size = PAGE_ALIGN(size);  

22.          if (!size || (size >> PAGE_SHIFT) > totalram_pages)  

23.              return NULL;  

24.          /*分配相关的结构并对其初始化,在前面介绍过了*/  

25.          area = __get_vm_area_node(size, align, VM_ALLOC, VMALLOC_START,  

26.                        VMALLOC_END, node, gfp_mask, caller);  

27.        

28.          if (!area)  

29.              return NULL;  

30.          /*分配物理空间,建立页表映射*/  

31.          addr = __vmalloc_area_node(area, gfp_mask, prot, node, caller);  

32.        

33.          /* 

34.           * A ref_count = 3 is needed because the vm_struct and vmap_area 

35.           * structures allocated in the __get_vm_area_node() function contain 

36.           * references to the virtual address of the vmalloc'ed block. 

37.           */  

38.           /*调试用*/  

39.          kmemleak_alloc(addr, real_size, 3, gfp_mask);  

40.        

41.          return addr;  

42.      }  

[cpp] view plaincopyprint?

static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,

pgprot_t prot, int node, void *caller)

1.            struct page **pages;  

2.            unsigned int nr_pages, array_size, i;  

3.            /*需要减去一个页面,因为在分配结构的时候指定了多一个页面*/  

4.            nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT;  

5.            /*页面指针所占空间大小*/  

6.            array_size = (nr_pages * sizeof(struct page *));  

7.          

8.            area->nr_pages = nr_pages;  

9.            /* Please note that the recursion is strictly bounded. */  

10.          if (array_size > PAGE_SIZE) {/*如果页面指针空间大于一个页面时,这个空间用非连续内存分配*/   

11.              pages = __vmalloc_node(array_size, 1, gfp_mask | __GFP_ZERO,  

12.                      PAGE_KERNEL, node, caller);  

13.              area->flags |= VM_VPAGES;  

14.          } else {/*如果页面指针空间所占大小小于一个页面时,用slab机制分配这个空间*/  

15.              pages = kmalloc_node(array_size,  

16.                      (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO,  

17.                      node);  

18.          }  

19.          /*初始化area结构*/  

20.          area->pages = pages;  

21.          area->caller = caller;  

22.          if (!area->pages) {  

23.              remove_vm_area(area->addr);  

24.              kfree(area);  

25.              return NULL;  

26.          }  

27.          /*对每个页面调用分配函数分配物理空间, 

28.          也就是每次分配一个页面*/  

29.          for (i = 0; i < area->nr_pages; i++) {  

30.              struct page *page;  

31.        

32.              if (node < 0)/*分配物理页面空间*/  

33.                  page = alloc_page(gfp_mask);  

34.              else  

35.                  page = alloc_pages_node(node, gfp_mask, 0);  

36.        

37.              if (unlikely(!page)) {  

38.                  /* Successfully allocated i pages, free them in __vunmap() */  

39.                  area->nr_pages = i;  

40.                  goto fail;  

41.              }  

42.              area->pages[i] = page;/*初始化areapage数组*/  

43.          }  

44.          /*因为非连续区间没有建立页表机制,在这里需要建立他*/  

45.          if (map_vm_area(area, prot, &pages))  

46.              goto fail;  

47.          return area->addr;/*返回线性地址*/  

48.        

49.      fail:  

50.          vfree(area->addr);  

51.          return NULL;  

52.      }  

其中map_vm_area()建立页表映射机制的实现就是依次对pgdpudpmdpte的设置。

二、非连续内存区的释放

调用vfree()函数实现

[cpp] view plaincopyprint?

1.        /** 

2.         *  vfree  -  release memory allocated by vmalloc() 

3.         *  @addr:      memory base address 

4.         * 

5.         *  Free the virtually continuous memory area starting at @addr, as 

6.         *  obtained from vmalloc(), vmalloc_32() or __vmalloc(). If @addr is 

7.         *  NULL, no operation is performed. 

8.         * 

9.         *  Must not be called in interrupt context. 

10.       */  

11.      void vfree(const void *addr)  

12.      {  

13.          BUG_ON(in_interrupt());  

14.          /*调试用*/  

15.          kmemleak_free(addr);  

16.          /*释放工作*/  

17.          __vunmap(addr, 1);  

18.      }  

[cpp] view plaincopyprint?

1.        static void __vunmap(const void *addr, int deallocate_pages)  

2.        {  

3.            struct vm_struct *area;  

4.          

5.            if (!addr)  

6.                return;  

7.          

8.            if ((PAGE_SIZE-1) & (unsigned long)addr) {  

9.                WARN(1, KERN_ERR "Trying to vfree() bad address (%p)\n", addr);  

10.              return;  

11.          }  

12.          /*vlist链表和红黑树中移除指定地址的线性区间*/  

13.          area = remove_vm_area(addr);  

14.          if (unlikely(!area)) {  

15.              WARN(1, KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n",  

16.                      addr);  

17.              return;  

18.          }  

19.        

20.          debug_check_no_locks_freed(addr, area->size);  

21.          debug_check_no_obj_freed(addr, area->size);  

22.        

23.          if (deallocate_pages) {  

24.              int i;  

25.        

26.              for (i = 0; i < area->nr_pages; i++) {/*每次释放一个页面*/  

27.                  struct page *page = area->pages[i];  

28.        

29.                  BUG_ON(!page);  

30.                  __free_page(page);  

31.              }  

32.        

33.              if (area->flags & VM_VPAGES)/*在创建非连续区间时,如果页面 

34.                  指针所占的空间大于一个页面时,从非连续内存区间 

35.                  中分配。所以这里也就从相应的释放*/  

36.                  vfree(area->pages);  

37.              else  

38.                  kfree(area->pages);/*slab中释放*/  

39.          }  

40.        

41.          kfree(area);/*释放area*/  

42.          return;  

43.      }  

[cpp] view plaincopyprint?

1.        /** 

2.         *  remove_vm_area  -  find and remove a continuous kernel virtual area 

3.         *  @addr:      base address 

4.         * 

5.         *  Search for the kernel VM area starting at @addr, and remove it. 

6.         *  This function returns the found VM area, but using it is NOT safe 

7.         *  on SMP machines, except for its size or flags. 

8.         */  

9.        struct vm_struct *remove_vm_area(const void *addr)  

10.      {  

11.          struct vmap_area *va;  

12.          /*从红黑树种查找而不是链表,为了效率起见*/  

13.          va = find_vmap_area((unsigned long)addr);  

14.          if (va && va->flags & VM_VM_AREA) {  

15.              struct vm_struct *vm = va->private;  

16.              struct vm_struct *tmp, **p;  

17.              /* 

18.               * remove from list and disallow access to this vm_struct 

19.               * before unmap. (address range confliction is maintained by 

20.               * vmap.) 

21.               */  

22.              write_lock(&vmlist_lock);  

23.              /*从链表中找到,然后删除*/  

24.              for (p = &vmlist; (tmp = *p) != vm; p = &tmp->next)  

25.                  ;  

26.              *p = tmp->next;  

27.              write_unlock(&vmlist_lock);  

28.              /*调试用*/  

29.              vmap_debug_free_range(va->va_start, va->va_end);  

30.              /*从红黑树中删除*/  

31.              free_unmap_vmap_area(va);  

32.              vm->size -= PAGE_SIZE;  

33.        

34.              return vm;  

35.          }  

36.          return NULL;  

37.      }  

总结:linux高端内存非连续区的整体描述以及其分配和释放基本就总结完了。总结的只是一个大概的原理框架,不过根据这个框架对细节的了解应该不难。另外,其中涉及到伙伴系统、slab机制等部分需要再做分析和总结。

阅读(1326) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~