当前位置:文档之家› linux 内存相关操作函数

linux 内存相关操作函数

Linux内核中内存相关的操作函数1、kmalloc()/kfree()static __always_inline void *kmalloc(size_t size, gfp_t flags)内核空间申请指定大小的内存区域,返回内核空间虚拟地址。

在函数实现中,如果申请的内存空间较大的话,会从buddy系统申请若干内存页面,如果申请的内存空间大小较小的话,会从slab系统中申请内存空间。

gfp_t flags 的选项较多。

参考内核文件gfp.h.在函数kmalloc()实现中,如果申请的空间较小,会根据申请空间的大小从slab中获取;如果申请的空间较大,如超过一个页面,会直接从buddy系统中获取。

2、vmalloc()/vfree()void *vmalloc(unsigned long size)函数作用:从高端(如果存在,优先从高端)申请内存页面,并把申请的内存页面映射到内核的动态映射空间。

vmalloc()函数的功能和alloc_pages(_GFP_HIGHMEM)+kmap() 的功能相似,只所以说是相似而不是相同,原因在于用vmalloc()申请的物理内存页面映射到内核的动态映射区(见下图),并且,用vmalloc()申请的页面的物理地址可能是不连续的。

而alloc_pages(_GFP_HIGHMEM)+kmap()申请的页面的物理地址是连续的,被映射到内核的KMAP区。

vmalloc分配的地址则限于vmalloc_start与vmalloc_end之间。

每一块vmalloc分配的内核虚拟内存都对应一个vm_struct结构体(可别和vm_area_struct搞混,那可是进程虚拟内存区域的结构),不同的内核虚拟地址被4k大小的空闲区间隔,以防止越界--见下图)。

与进程虚拟地址的特性一样,这些虚拟地址与物理内存没有简单的位移关系,必须通过内核页表才可转换为物理地址或物理页。

它们有可能尚未被映射,在发生缺页时才真正分配物理页面。

如果内存紧张,连续区域无法满足,调用vmalloc分配是必须的,因为它可以将物理不连续的空间组合后分配,所以更能满足分配要求。

vmalloc可以映射高端页框,也可以映射底端页框。

vmalloc的作用只是为了提供逻辑上连续的地址…注意:在申请页面时,如果注明_GFP_HIGHMEM,即从高端申请。

则实际是优先从高端内存申请,顺序为(分配顺序是HIGH, NORMAL, DMA )。

3、alloc_pages()/free_pages()内核空间申请指定个数的内存页,内存页数必须是2^order个页。

alloc_pages(gfp_mask, order) 中,gfp_mask 是flag标志,其中可以为_ _GFP_DMA、_GFP_HIGHMEM 分别对应DMA和高端内存。

注:该函数基于buddy系统申请内存,申请的内存空间大小为2^order个内存页面。

参见《linux内核之内存管理。

doc》通过函数alloc_pages()申请的内存,需要使用kmap()函数分配内核的虚拟地址。

4、__get_free_pages()/__free_pages()unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)作用相当于alloc_pages(NORMAL)+kmap(),但不能申请高端内存页面。

__get_free_page()只申请一个页面。

5、kmap()/kunmap()返回指定页面对应内核空间的虚拟地址。

#includevoid *kmap(struct page *page);void kunmap(struct page *page);kmap 为系统中的任何页返回一个内核虚拟地址。

对于低端内存页,它只返回页的逻辑地址;对于高端内存页,kmap在"内核永久映射空间"中创建一个特殊的映射。

这样的映射数目是有限,因此最好不要持有过长的时间。

使用kmap 创建的映射应当使用kunmap 来释放;kmap 调用维护一个计数器,因此若2个或多个函数都在同一个页上调用kmap也是允许的。

通常情况下,"内核永久映射空间"是4M 大小,因此仅仅需要一个页表即可,内核通过来pkmap_page_table 寻找这个页表。

注意:不用时及时释放。

kmalloc()和vmalloc()相比,kmalloc()总是从ZONE_NORMAL(下图中的直接映射区)申请内存。

kmalloc()分配的内存空间通常用于linux内核的系统数据结构和链表。

因内核需要经常访问其数据结构和链表,使用固定映射的ZONE_NORMAL空间的内存有利于提高效率。

使用vmalloc()可以申请非连续的物理内存页,并组成虚拟连续内存空间。

vmalloc()优先从高端内存(下图中的动态映射区)申请。

内核在分配那些不经常使用的内存时,都用高端内存空间(如果有),所谓不经常使用是相对来说的,比如内核的一些数据结构就属于经常使用的,而用户的一些数据就属于不经常使用的。

alloc_pages(_GFP_HIGHMEM)+kmap() 方式申请的内存使用内核永久映射空间(下图中的KMAP区),空间较小(通常4M线性空间),不用时需要及时释放。

另外,可以指定alloc_pages()从直接映射区申请内存,需要使用_GFP_NORMAL属性指定。

__get_free_pages()/__free_pages() 不能申请高端内存页面,操作区域和kmalloc()相同(下图中的动态映射区)。

6、virt_to_page()其作用是由内核空间的虚拟地址得到页结构。

见下面的宏定义。

#define virt_to_pfn(kaddr) (__pa(kaddr) 》PAGE_SHIFT)#define pfn_to_virt(pfn) __va((pfn) 《PAGE_SHIFT)#define virt_to_page(addr) pfn_to_page(virt_to_pfn(addr))#define page_to_virt(page) pfn_to_virt(page_to_pfn(page))#define __pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET))#define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + \ARCH_PFN_OFFSET)7、物理地址和虚拟地址之间转换#ifdef CONFIG_BOOKE#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) + VIRT_PHYS_OFFSET))#define __pa(x) ((unsigned long)(x) - VIRT_PHYS_OFFSET)#else#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) + PAGE_OFFSET - MEMORY_START))#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET + MEMORY_START)#endif8、ioremap()/iounmap()ioremap()的作用是把device寄存器和内存的物理地址区域映射到内核虚拟区域,返回值为内核的虚拟地址。

注明:在内核中操作内存空间时使用的都是内核虚拟地址,必须把device的空间映射到内核虚拟空间。

#includevoid *ioremap(unsigned long phys_addr, unsigned long size);void *ioremap_nocache(unsigned long phys_addr, unsigned long size); 映射非cache的io 内存区域void iounmap(void * addr);为了增加可移植性,最好使用下面的接口函数读写io内存区域,unsigned int ioread8(void *addr);unsigned int ioread16(void *addr);unsigned int ioread32(void *addr);void iowrite8(u8 value, void *addr);void iowrite16(u16 value, void *addr);void iowrite32(u32 value, void *addr);如果你必须读和写一系列值到一个给定的I/O 内存地址,你可以使用这些函数的重复版本:void ioread8_rep(void *addr, void *buf, unsigned long count);void ioread16_rep(void *addr, void *buf, unsigned long count);void ioread32_rep(void *addr, void *buf, unsigned long count);void iowrite8_rep(void *addr, const void *buf, unsigned long count);void iowrite16_rep(void *addr, const void *buf, unsigned long count);void iowrite32_rep(void *addr, const void *buf, unsigned long count);这些函数读或写count 值从给定的buf 到给定的addr. 注意count 表达为在被写入的数据大小; ioread32_rep 读取count 32-位值从buf 开始。

9、request_mem_region()本函数的作用是:外设的io端口映射到io memory region中。

在本函数实现中会检查输入到本函数的参数所描述的空间(下面成为本io空间)是否和io memory region中已存在的空间冲突等,并设置本io空间的parent字段等(把本io空间插入到io 空间树种)。

注明:io memory region 空间中是以树形结构组织的,默认的根为iomem_resource描述的io空间,其name为"PCI mem".request_mem_region(start,n,name) 输入的参数依次是设备的物理地址,字节长度,设备名字。

相关主题