linux内存学习笔记(一)

来源:互联网 发布:淘宝服装质检标准 编辑:程序博客网 时间:2024/06/06 20:03
 

一、内存管理单元MMU

该单元辅助操作系统进行内存管理,提供虚拟地址和物理地址的映射、内存访问权限保护和Cache缓存控制等硬件支持。

(1)       TLB:它缓存少量的虚拟地址与物理地址的转换关系。“快表”

(2)       TTW:包含所有虚拟地址与物理地址的转换关系。TTW成功后,结果写入TLB

二、arm平台linux内存映射(来自《解析基于ARM9 的Linux 内存映射》)

arm中的地址转换表不是一下子就建立的,分为三个阶段

(1)       第一阶段是发生在内核解压缩,自引导时,也就内核镜像zimage 的文件头部分。在/arch/arm/boot/compressed/head.S中实现,这个映射表是临时的,是为了提高内核解压缩时的速度而实现的。在解压缩结束之后,进入内核代码之前,MMU 功能就被关闭了,随之的映射表也被废弃不用。

(2)       第二阶段是的页表创建是非常关键的。同样也是使用汇编语言来实现,具体文件路径为:/arch/arm/kernel/head.S。在代码,有个函数__create_page_tables,这就是创建MMU 映射表,开启MMU 做准备的重要动作

(3)       第三阶段是创建更高级别的映射关系和映射方法,主要和架构相关。不同的架构有不同的映射方法,比如ARM 就是二级映射的方法实现地址映射,但是必须按照LINUX 的三级映射模型PGD、PMD、PTE 来实现。从内核代码上来看,内核启动的过程中主要有两个函数比较重要:(1)start_kernel->setup_arch->paging_init->bootmem_init->bootmem_init_node->create_mapping(2)start_kernel->setup_arch->paging_init->devicemaps_init(3)start_kernel->trap_init函数一:重新建立了一级映射表,添加了对内存空间的二级映射,同时建立了bootmem 这个内存管理器。函数二:在再bootmem 的帮助下实现了平台部分设备IO 地址的映射。函数三:则是完成了中断矢量的映射。

三、用户空间内动态申请

#include <stdlib.h>

void *malloc(size_t size);

void free(void *ptr_to memory);

四、内核空间内存动态申请

void * kmalloc(size_t size,int flags);

kmalloc()的底层依赖__get_free_pages()实现。最常用的分配标志GFP_KERNEL,其含义为在内核空间的进程中申请内存。该函数标志可引起进程阻塞。

GFP_ATOMIC,不会引起阻塞。

GFP_USER,为用户空间分配内存。

GFP_HIGHUSER,类似GFP_USER,但是从高端分配内存。

GFP_NOIO,不允许I/O初始化

GFP_NOFS,不允许进行任何文件系统调用。

__GFP_DMA,要求分配在能够DMA的内存区。

__GFP_HIGHMEM,指示分配的内存可以位于高端内存。

__GFP_COLD,请求一个较长时间不访问的页。

__GFP_NOWARN,分配无法满足时,阻止内核发出警告。

__GFP_HIGH,高优先级请求,允许获得被内核保留给紧急状态使用的最后的内存页。

__GFP_REPEAT,分配失败则尽力重复尝试。

__GFP_NOFAIL,标志只允许申请成功,不推荐。

__GFP_NORETRY,若申请不成功,则放弃。

 

__get_free_pages()系列函数

get_zeroed_page(unsigned int flags);

返回一个指向新页的指针,并将该页清零。

__get_free_page(unsigned int flags);

返回一个指向新页的指针,不清零。

__get_free_pages(unsigned int flags,unsigned int order);

返回可分配多个页并返回分配内存的首地址,分配的页数为2的order次方。

void free_page(unsigned long addr);

void free_pages(unsigned long addr,unsigned long order);

flags的参数说明与kmalloc()的flags一致。

 

 

void *vmalloc(unsigned long size);

void vfree(void *addr);

此函数一般用于分配较大的缓冲区,分配少量的内存是不合理的

 

 

slab与内存池

slab在底层每次申请1页或多页,之后再分隔这些页为更小的单元,从而节省了内存。内存池和slab设计目的相仿。

创建slab

struct kmem_cache *kmem_cache_create(const char *name,

size_t size,size_t align,unsigned long flags,

void (*ctor)(void *,struct kmem_cache *,unsigned long),

void (*dtor)(void *,struct kmem_cache *,unsigned long));

 

              flags,SLAB_NO_REAP,即使内存紧缺时也不自动收缩这块缓存。

                     SLAB_HWCACHE_ALIGN,每个数据对象被对齐到一人缓存行。

                     SLAB_CACHE_DMA,要求数据对象在DMA内存区分配。

       分配slab缓存

              void *kmem_cache_alloc(struct kmem_cache *cachep,gfp_t flags);

       释放

              void kmem_cache_free(struct kmem_cache *cachep,void *objp);

       回收

              int kmem_cache_destroy(struct kmem_cache *cachep);

slab使用模板

              static kmem_cache_t *xxx_cachep;

              xxx_cachep=kmem_cache_create(“xxx”,sizeof(struct xxx),0,

                                                 SLAB_HWCACHE_ALIGN|SLAB_PANIC,NULL,NULL);

              struct xxx *ctx;

              ctx=kmem_cache_alloc(xxx_cachep,GFP_KERNEL);

              kmem_cache_free(xxx_cachep,ctx);

              kmem_cache_destroy(xxx_cachep);

       在/proc/slabinfo节点中可以获知当前slab的分配和使用情况。

创建内存池

       mempoo_t *mempool_create(int min_nr,mempool_alloc_t *alloc_fn,

                            mempool_free_t *free_fn,void *pool_data);

min_nr,预分配对象的数目

alloc_fn的free_fn,内存池对象分配和回收函数的指针。

pool_data,是分配和回收函数要用到的指针。

       分配和回收对象

       void *mempool_alloc(mempool_t *pool,int gfp_mask);

       void *mempool_free(void *element,mempool_t *pool);

gfp_mask,分配标记,只有当__GFP_WAIT标记被指定时,分配函数才会休眠。

       回收内存池

       void mempool_destroy(mempool_t *pool);

 

五、I/O内存

动态映射

void *ioremap(unsigned long offset,unsigned long size);

void inunmap(void *addr);

访问

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);

 

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,void *buf,unsigned long count);

void iowrite16_rep(void *addr,void *buf,unsigned long count);

void iowrite32_rep(void *addr,void *buf,unsigned long count);

 

void memcpy_fromio(void *dest,void *source,unsigned int count);

void memcpy_toio(void *dest,void *source,unsigned int count);

 

I/O内存申请

struct resource *request_mem_region(unsigned long start,unsigned long len,char *name);

void release_mem_region(unsigned long start,unsigned long len);

上面两个函数是检查申请的资源是否可用,如果可用再进行ioremap()申请,这样会访问会相对安全一些。

 

 

原创粉丝点击