收藏 分享(赏)

kmalloc vmalloc.docx

上传人:hwpkd79526 文档编号:8090220 上传时间:2019-06-08 格式:DOCX 页数:7 大小:24.31KB
下载 相关 举报
kmalloc vmalloc.docx_第1页
第1页 / 共7页
kmalloc vmalloc.docx_第2页
第2页 / 共7页
kmalloc vmalloc.docx_第3页
第3页 / 共7页
kmalloc vmalloc.docx_第4页
第4页 / 共7页
kmalloc vmalloc.docx_第5页
第5页 / 共7页
点击查看更多>>
资源描述

1、kmalloc vmalloc 在设备驱动程序中动态开辟内存,不是用 malloc,而是 kmalloc,或者用 get_free_pages直接申请页。释放内存用的是 kfree,或 free_pages.对于提供了 MMU(存储管理器,辅助操作系统进行内存管理,提供虚实地址转换等硬件支持)的处理器而言,Linux 提供了复杂的存储管理系统,使得进程所能访问的内存达到 4GB。进程的 4GB 内存空间被人为的分为两个部分 -用户空间与内核空间。用户空间地址分布从0 到 3GB(PAGE_OFFSET,在 0x86 中它等于 0xC0000000),3GB 到 4GB 为内核空间。内核空间中,

2、从 3G 到 vmalloc_start 这段地址是物理内存映射区域(该区域中包含了内核镜像、物理页框表 mem_map 等等),比如我们使用的 VMware 虚拟系统内存是160M,那么 3G3G+160M 这片内存就应该映射物理内存。在物理内存映射区之后,就是vmalloc 区域。对于 160M 的系统而言,vmalloc_start 位置应在 3G+160M 附近(在物理内存映射区与 vmalloc_start 期间还存在一个 8M 的 gap 来防止跃界),vmalloc_end 的位置接近 4G(最后位置系统会保留一片 128k 大小的区域用于专用页面映射)kmalloc 和 get

3、_free_page 申请的内存位于物理内存映射区域,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因此存在较简单的转换关系,virt_to_phys()可以实现内核虚拟地址转化为物理地址:#define _pa(x) (unsigned long)(x)-PAGE_OFFSET)extern inline unsigned long virt_to_phys(volatile void * address)return _pa(address);上面转换过程是将虚拟地址减去 3G(PAGE_OFFSET=0XC000000)。与之对应的函数为 phys_to_virt(),

4、将内核物理地址转化为虚拟地址:#define _va(x) (void *)(unsigned long)(x)+PAGE_OFFSET)extern inline void * phys_to_virt(unsigned long address)return _va(address);virt_to_phys()和 phys_to_virt()都定义在 includeasm-i386io.h 中。-1、kmalloc() 分配连续的物理地址,用于小内存分配。 2、 _get_free_page() 分配连续的物理地址,用于整页分配。 至于为什么说以上函数分配的是连续的物理地址和返回的到底是

5、物理地址还是虚拟地址,下面的记录会做出解释。 kmalloc() 函数本身是基于 slab 实现的。slab 是为分配小内存提供的一种高效机制。但 slab 这种分配机制又不是独立的,它本身也是在页分配器的基础上来划分更细粒度的内存供调用者使用。也就是说系统先用页分配器分配以页为最小单位的连续物理地址,然后 kmalloc() 再在这上面根据调用者的需要进行切分。 关于以上论述,我们可以查看 kmalloc() 的实现,kmalloc()函数的实现是在 _do_kmalloc() 中,可以看到在 _do_kmalloc()代码里最终调用了 _cache_alloc() 来分配一个 slab,其

6、实 kmem_cache_alloc() 等函数的实现也是调用了这个函数来分配新的 slab。我们按照 _cache_alloc()函数的调用路径一直跟踪下去会发现在 cache_grow() 函数中使用了 kmem_getpages()函数来分配一个物理页面, kmem_getpages() 函数中调用的alloc_pages_node() 最终是使用 _alloc_pages() 来返回一个 struct page 结构,而这个结构正是系统用来描述物理页面的。这样也就证实了上面所说的,slab 是在物理页面基础上实现的。kmalloc() 分配的是物理地址。 _get_free_page(

7、) 是页面分配器提供给调用者的最底层的内存分配函数。它分配连续的物理内存。_get_free_page() 函数本身是基于 buddy 实现的。在使用 buddy 实现的物理内存管理中最小分配粒度是以页为单位的。关于以上论述,我们可以查看_get_free_page()的实现,可以看到_get_free_page()函数只是一个非常简单的封状,它的整个函数实现就是无条件的调用 _alloc_pages() 函数来分配物理内存,上面记录 kmalloc()实现时也提到过是在调用 _alloc_pages() 函数来分配物理页面的前提下进行的 slab 管理。那么这个函数是如何分配到物理页面又是在

8、什么区域中进行分配的?回答这个问题只能看下相关的实现。可以看到在 _alloc_pages() 函数中,多次尝试调用get_page_from_freelist() 函数从 zonelist 中取得相关 zone,并从其中返回一个可用的 struct page 页面(这里的有些调用分支是因为标志不同)。至此,可以知道一个物理页面的分配是从 zonelist(一个 zone 的结构数组)中的 zone 返回的。那么 zonelist/zone 是如何与物理页面关联,又是如何初始化的呢?继续来看 free_area_init_nodes() 函数,此函数在系统初始化时由 zone_sizes_in

9、it() 函数间接调用的,zone_sizes_init() 函数填充了三个区域:ZONE_DMA ,ZONE_NORMAL,ZONE_HIGHMEM。并把他们作为参数调用 free_area_init_nodes(),在这个函数中会分配一个 pglist_data 结构,此结构中包含了 zonelist/zone 结构和一个 struct page 的物理页结构,在函数最后用此结构作为参数调用了 free_area_init_node() 函数,在这个函数中首先使用 calculate_node_totalpages() 函数标记 pglist_data 相关区域,然后调用 alloc_no

10、de_mem_map() 函数初始化 pglist_data 结构中的 struct page 物理页。最后使用 free_area_init_core()函数关联 pglist_data 与 zonelist。现在通以上分析已经明确了_get_free_page() 函数分配物理内存的流程。但这里又引出了几个新问题,那就是此函数分配的物理页面是如何映射的?映射到了什么位置?到这里不得不去看下与 VMM 相关的引导代码。 在看 VMM 相关的引导代码前,先来看一下 virt_to_phys() 与 phys_to_virt 这两个函数。顾名思义,即是虚拟地址到物理地址和物理地址到虚拟地址的转换

11、。函数实现十分简单,前者调用了_pa( address ) 转换虚拟地址到物理地址,后者调用 _va(addrress ) 将物理地址转换为虚拟地址。再看下 _pa _va 这两个宏到底做了什么。 #define _pa(x) (unsigned long)(x)-PAGE_OFFSET) #define _va(x) (void *)(unsigned long)(x)+PAGE_OFFSET) 通过上面可以看到仅仅是把地址加上或减去 PAGE_OFFSET,而 PAGE_OFFSET 在 x86 下定义为 0xC0000000。这里又引出了疑问,在 linux 下写过 driver 的人都

12、知道,在使用 kmalloc() 与 _get_free_page() 分配完物理地址后,如果想得到正确的物理地址需要使用 virt_to_phys() 进行转换。那么为什么要有这一步呢?我们不分配的不就是物理地址么?怎么分配完成还需要转换?如果返回的是虚拟地址,那么根据如上对 virt_to_phys() 的分析,为什么仅仅对 PAGE_OFFSET 操作就能实现地址转换呢?虚拟地址与物理地址之间的转换不需要查页表么?代着以上诸多疑问来看 VMM 相关的引导代码。 直接从 start_kernel() 内核引导部分来查找 VMM 相关内容。可以看到第一个应该关注的函数是 setup_arch

13、(),在这个函数当中使用 paging_init() 函数来初始化和映射硬件页表(在初始化前已有 8M 内存被映射,在这里不做记录),而 paging_init() 则是调用的pagetable_init() 来完成内核物理地址的映射以及相关内存的初始化。在 pagetable_init() 函数中,首先是一些 PAE/PSE/PGE 相关判断与设置,然后使用 kernel_physical_mapping_init() 函数来实现内核物理内存的映射。在这个函数中可以很清楚的看到,pgd_idx 是以 PAGE_OFFSET 为启始地址进行映射的,也就是说循环初始化所有物理地址是以 PAGE_

14、OFFSET 为起点的。继续观察我们可以看到在 PMD 被初始化后,所有地址计算均是以 PAGE_OFFSET 作为标记来递增的。分析到这里已经很明显的可以看出,物理地址被映射到以 PAGE_OFFSET 开始的虚拟地址空间。这样以上所有疑问就都有了答案。kmalloc() 与_get_free_page() 所分配的物理页面被映射到了 PAGE_OFFSET 开始的虚拟地址,也就是说实际物理地址与虚拟地址有一组一一对应的关系, 正是因为有了这种映射关系,对内核以 PAGE_OFFSET 启始的虚拟地址的分配也就是对物理地址的分配(当然这有一定的范围,应该在 PAGE_OFFSET 与 VMA

15、LLOC_START 之间,后者为 vmalloc() 函数分配内存的启始地址)。这也就解释了为什么 virt_to_phys() 与 phys_to_virt() 函数的实现仅仅是加/减 PAGE_OFFSET 即可在虚拟地址与物理地址之间转换,正是因为了有了这种映射,且固定不变,所以才不用去查页表进行转换。这也同样回答了开始的问题,即 kmalloc() / _get_free_page() 分配的是物理地址,而返回的则是虚拟地址(虽然这听上去有些别扭)。正是因为有了这种映射关系,所以需要将它们的返回地址减去 PAGE_OFFSET 才可以得到真正的物理地址。另一篇更容易理解的:kmall

16、oc, vmalloc 分配的内存结构 zz2008-01-20 16:05进程空间:| | |内核空间:| | |=原文= 对于提供了 MMU(存储管理器,辅助操作系统进行内存管理,提供虚实地址转换等硬件支持)的处理器而言,Linux 提供了复杂的存储管理系统,使得进程所能访问的内存达到 4GB。进程的 4GB 内存空间被人为的分为两个部分-用户空间与内核空间。用户空间地址分布从 0到 3GB(PAGE_OFFSET,在 0x86 中它等于 0xC0000000),3GB 到 4GB 为内核空间。内核空间中,从 3G 到 vmalloc_start 这段地址是物理内存映射区域(该区域中包含了

17、内核镜像、物理页框表 mem_map 等等),比如我们使用 的 VMware 虚拟系统内存是 160M,那么 3G3G+160M 这片内存就应该映射物理内存。在物理内存映射区之后,就是 vmalloc 区域。对于 160M 的系统而言,vmalloc_start 位置应在 3G+160M 附近(在物理内存映射区与 vmalloc_start 期间还存在一个 8M 的 gap 来防止跃界),vmalloc_end 的位置接近4G(最后位置系统会保留一片 128k 大小的区域用于专用页面映射)kmalloc 和 get_free_page 申请的内存位于物理内存映射区域,而且在物理上也是连续的,它

18、们与真实的物理地址只有一个固定的偏移,因此存在较简单的转换关系,virt_to_phys()可以实现内核虚拟地址转化为物理地址:#define _pa(x) (unsigned long)(x)-PAGE_OFFSET)extern inline unsigned long virt_to_phys(volatile void * address)return _pa(address);上面转换过程是将虚拟地址减去 3G(PAGE_OFFSET=0XC000000)。与之对应的函数为 phys_to_virt(),将内核物理地址转化为虚拟地址:#define _va(x) (void *)(u

19、nsigned long)(x)+PAGE_OFFSET)extern inline void * phys_to_virt(unsigned long address)return _va(address);virt_to_phys()和 phys_to_virt()都定义在 includeasm-i386io.h 中。而 vmalloc 申请的内存则位于 vmalloc_startvmalloc_end 之间,与物理地址没有简单的转换关系,虽然在逻辑上它们也是连续的,但是在物理上它们不要求连续。我们用下面的程序来演示 kmalloc、get_free_page 和 vmalloc 的区别:

20、#include #include #include MODULE_LICENSE(“GPL“);unsigned char *pagemem;unsigned char *kmallocmem;unsigned char *vmallocmem;int _init mem_module_init(void)/最好每次内存申请都检查申请是否成功/下面这段仅仅作为演示的代码没有检查pagemem = (unsigned char*)get_free_page(0);printk(“pagemem addr=%x“, pagemem);kmallocmem = (unsigned char*)km

21、alloc(100, 0);printk(“kmallocmem addr=%x“, kmallocmem);vmallocmem = (unsigned char*)vmalloc(1000000);printk(“vmallocmem addr=%x“, vmallocmem);return 0;void _exit mem_module_exit(void)free_page(pagemem);kfree(kmallocmem);vfree(vmallocmem);module_init(mem_module_init);module_exit(mem_module_exit);我们的系统上有 160MB 的内存空间,运行一次上述程序,发现 pagemem 的地址在0xc7997000(约 3G+121M)、kmallocmem 地址在 0xc9bc1380(约 3G+155M)、vmallocmem 的地址在 0xcabeb000(约 3G+171M)处,符合前文所述的内存布局。 本文来自 CSDN 博客,转载请标明出处:http:/

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报