当前位置: 首页 > 图文教程 > 操作系统 > Unix/Linux > 内核中的物理内存分配函数kernel api

Unix/Linux
Linux 备份 恢复方法
Linux玩CS反恐精英的方法
在一个ISO镜像中集成多个不同的linux发行版,可刻盘,可引导
Linux 快捷键使用
Linux DHCP 服务器配置方法介绍
Linux 22端口的修改方法
Linux 记录会话过程的命令
Linux 后台执行程序如何操作?
linux Wget命令来浏览网页的方法
Linux tail命令的巧妙应用
Wine 中文存在很多的乱码怎么解决方法
linux 新手教程之创建锁文件的方法
配置Linux 保证其系统的安全
Linux DHCP协议实现过程
Linux系统下破解SAM密码
linux/unix vi 编辑器用法详解
Linux 误删文件的解决方法
Linux系统下的历史记录删除方法
Red Hat Linux 安全设置指南
Linux基本命令-注销、关机、重启

Unix/Linux 中的 内核中的物理内存分配函数kernel api


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-11-01   浏览: 260 ::
收藏到网摘: n/a

在网上查资料时看到几篇介绍 linux driver 编写的文章,其中提到 kmalloc()与 __get_free_page()返回地址的问题,我们都知道 kmalloc() 与 __get_free_page() 分配的是物理内存,但它返回的到底是什么?那几篇关于驱动编写的文章中提到申请的是物理地址,返回的依然是物理地址。但有一篇文章中,作者

对此提出了质疑,但没有给出答案。这也就是我写这篇笔记的原因。在找答案的同时也将 linux kernel 分配物理内存的流程做了下分析。这仅是篇笔记,写的比较乱。自己能看懂就行了。

这里只分析分配连续物理地址的函数。对于 vmalloc() 这种分配非连续物理地址的函数不在本记录范围之内。

1、kmalloc() 分配连续的物理地址,用于小内存分配。

2、__get_free_page() 分配连续的物理地址,用于整页分配。

至于为什么说以上函数分配的是连续的物理地址和返回的到底是物理地址还是虚拟地址,下面的记录会做出解释。

kmalloc() 函数本身是基于 slab 实现的。slab 是为分配小内存提供的一种高效机制。但 slab 这种分配机制又不是独立的,它本身也是在页分配器的基础上来划分更细粒度的内存供调用者使用。也就是说系统先用页分配器分配以页为最小单位的连续物理地址,然后 kmalloc() 再在这上面根据调用者的需要进行切分。

关于以上论述,我们可以查看 kmalloc() 的实现,kmalloc()函数的实现是在 __do_kmalloc() 中,可以看到在 __do_kmalloc()代码里最终调用了 __cache_alloc() 来分配一个 slab,其实

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

__get_free_page() 是页面分配器提供给调用者的最底层的内存分配函数。它分配连续的物理内存。__get_free_page() 函数本身是基于 buddy 实现的。在使用 buddy 实现的物理内存管理中最小分配粒度是以页为单位的。关于以上论述,我们可以查看__get_free_page()的实现,可以看到__get_free_page()函数只是一个非常简单的封状,它的整个函数实现就是无条件的调用 __alloc_pages() 函数来分配物理内存,上面记录 kmalloc()实现时也提到过是在调用 __alloc_pages() 函数来分配物理页面的前提下进行的 slab 管理。那么这个函数是如何分配到物理页面又是在什么区域中进行分配的?回答这个问题只能看下相关的实现。可以看到在 __alloc_pages() 函数中,多次尝试调用get_page_from_freelist() 函数从 zonelist 中取得相关 zone,并从其中返回一个可用的 struct page 页面(这里的有些调用分支是因为标志不同)。至此,可以知道一个物理页面的分配是从 zonelist(一个 zone 的结构数组)中的 zone 返回的。那么 zonelist/zone 是如何与物理页面关联,又是如何初始化的呢?继续来看 free_area_init_nodes() 函数,此函数在系统初始化时由 zone_sizes_init() 函数间接调用的,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_node_mem_map() 函数初始化 pglist_data结构中的 struct page 物理页。最后使用 free_area_init_core()函数关联 pglist_data 与 zonelist。现在通以上分析已经明确了__get_free_page() 函数分配物理内存的流程。但这里又引出了几个新问题,那就是此函数分配的物理页面是如何映射的?映射到了什么位置?到这里不得不去看下与 VMM 相关的引导代码。

在看 VMM 相关的引导代码前,先来看一下 virt_to_phys() 与phys_to_virt 这两个函数。顾名思义,即是虚拟地址到物理地址和物理地址到虚拟地址的转换。函数实现十分简单,前者调用了__pa( address ) 转换虚拟地址到物理地址,后者调用 __va(a