zoukankan      html  css  js  c++  java
  • 内存管理

    一、内存管理的相关概念
    内核把物理页作为内存管理的基本单位。
    内核用struct page结构表示系统中的每个物理页。
    内核使用区对具有相似特性的页进行分组。
    内核把页划分为不同的区,主要使用了四种区:ZONE_DMA、ZONE_DMA32、ZONE_NORMAL、ZONE_HIGHEM。

    二、获得页的接口

    struct page * alloc_pages(gfp_t gfp_mask, unsigned int order);
    //该函数分配2的order次方即1<<order个连续的物理页;
    //并返回一个指针,该指针指向第一个页的page结构体;
    //如果出错,就返回NULL。
    
    void * page_address(struct page * page);
    //该函数返回一个指针,指向给定物理页当前所在的逻辑地址。
    
    unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
    //这个函数与alloc_pages()作用相同,不过它直接返回所请求的第一个页的逻辑地址。
    
    struct page * alloc_page(gfp_t gfp_mask);
    unsigned long __get_free_page(gfp_mask);
    //这两个函数同上面两个函数工作方式相同,只不过传递给order的值为0,即分配一页。
    
    unsigned long get_zeroed_page(unsigned int gfp_mask);
    //只分配一页,让其内容填充0,返回指向其逻辑地址的指针。
    
    void __free_pages(struct page * page, unsigned int order);
    void __free_page(struct page * page);
    void free_pages(unsigned long addr, unsigned int order);
    void free_page(unsigned long addr);
    //释放分配的页
    

    三、kmalloc()

    kmalloc()在<linux/slab.h>中声明。

    void * kmalloc(size_t size, gfp_t flags);
    //这个函数返回一个指向内存块的指针,其内存块至少要有size大小;
    //所分配的内存区在物理上是连续的;
    //出错时返回NULL
    
    void kfree(const void * ptr);
    //kfree()函数释放由kmalloc()分配出来的内存块。
    
    gfp_mask标志
    这些标志可以分为三类:行为修饰符、区修饰符、类型。
    
    行为修饰符表示内核应当如何分配所需的内存。
    //例如,中断处理程序就要求内核在分配内存的过程中不能睡眠。
    __GFP_WAIT、__GFP_HIGH、__GFP_IO、__GFP_FS、__GFP_COLD、__GFP_NOWARN、
    __GFP_REPEAT、__GFP_NOFALL、__GFP_NORETRY、__GFP_NO_GROW、__GFP_COMP
    
    区修饰符表示内存区应当从何处分配。
    __GFP_DMA、__GFP_DMA32、__GFP_HIGHMEM
    
    类型标志指定所需的行为和区描述符以完成特殊类型的处理。
    内核代码趋向于使用正确的类型标志,而不是一味地指定它可能需要用到的多个描述符。
    GFP_ATOMIC、GFP_NOWAIT、GFP_NOIO、GFP_NOFS、GFP_KERNEL、GFP_USER、GFP_HIGHUSER、GFP_DMA
    

    四、vmalloc()
    vmalloc()函数声明在<linux/vmalloc.h>中,定义在<mm/vmalloc.c>中。
    用法与用户空间的malloc()相同。

    注意:vmalloc()函数的工作方式类似于kmalloc(),只不过前者分配的内存虚拟地址是连续的,而物理地址则无需连续。

    void * vmalloc(unsigned long size);
    //返回一个指针,指向逻辑上连续的一块内存区,其大小至少为size。
    //函数可能睡眠
    
    void free(const void *addr);
    //释放通过vmalloc()函数分配的内存
    

    五、slab层

    slab分配器扮演了通用数据结构缓冲层的角色。
    slab层把不同的对象划分为所谓高速缓冲组,其中每个高速缓冲都存放不同类型的对象。
    slab由一个或多个物理上连续的页组成。一般情况下仅由一页组成。
    每个slab处于三种状态之一:满、部分满、空。

    每种对象类型对应一个高速缓存;
    每个高速缓存可以由多个slab组成;
    每个slab都包含一些对象成员,这里的对象指的是被缓存的数据结构。

    高速缓存使用struct kmem_cache结构表示;
    slab描述符struct slab用来描述每个slab。

    接口函数:
    struct kmem_cache * kmem_cache_create(const char * name, size_t size, size_t align,
    				unsigned long flags, void (*ctor) (void *));
    				//第一个参数是一个字符串,存放着高速缓存的名字;
    				//第二个参数是高速缓存中每个元素的大小;
    				//第三个参数是slab内第一个对象的偏移,它用来确保在页内进行特定的对齐。通常0就可以满足。
    				//第四个参数是可选的设置项,用来控制高速缓存的行为。可以为0,表示没有特殊的行为。
    								//或者以下标志中的一个或多个进行“或”运算:
    								//SLAB_HWCACHE_ALIGN			把一个slab内的所有对象按高速缓存行对齐。
    								//SLAB_POISON				用已知的值(a5a5a5a5)填充slab。
    								//SLAB_RED_ZONE				在已分配的内存周围插入“红色警戒区”以探测缓冲越界。
    								//SLAB_PANIC				当分配失败时提醒slab层。
    								//SLAB_CACHE_DMA			使用可以执行DMA的内存给每个slab分配空间。
    				//第五个参数是高速缓存的构造函数。由于内核代码并不需要它,因此已经被抛弃了。
    				注意:该函数可以引起睡眠。
    				
    int kmem_cache_destroy(struct kmem_cache * cachep);
    				//撤销给定的高速缓存。
    				注意:该函数也可能睡眠。
    				
    void * kmem_cache_alloc(struct kmem_cache * cachep, gfp_t flags);
    				//从给定的高速缓存cachep中返回一个指向对象的指针。
    void * kmem_cache_free(struct kmem_cache * cachep, void * objp);
    				//释放一个对象,并把它返回给原先的slab。
    				
    

    六、高端内存的映射

    1.高端内存的映射
    void * kmap(struct page * page);
    				//映射一个给定的page结构到内核地址空间;这个函数在高端内存或低端内存上都能用。
    				//这个函数可以睡眠
    				
    void kunmap(struct page * page);
    				//接触映射。允许永久映射的数量是有限的,当不再需要高端内存时,应该解除映射。
    
    2.临时映射
    void * kmap_atomic(struct page * page, enum km_type type);
    void kunmap_atomic(void * kvaddr, enum km_type type);
    				//建立和取消一个临时映射,这两个函数不会导致睡眠。
    
    enum km_type{
    		KM_BOUNCE_READ,
    		KM_SKB_SUNRPC_DATA,
    		KM_SKB_DATA_SOFTIRQ,
    		KM_USER0,
    		KM_USER1,
    		KM_BIO_SRC_IRQ,
    		KM_BIO_DST_IRQ,
    		KM_PTE0,
    		KM_PTE1,
    		KM_PTE2,
    		KM_IRQ0,
    		KM_IRQ1,
    		KM_SOFTIRQ0,
    		KM_SOFTIRQ1,
    		KM_SYNC_ICACHE,
    		KM_SYNC_DCACHE,
    		KM_UML_USERCOPY,
    		KM_IRQ_PTE,
    		KM_NMI,
    		KM_NMI_PTE,
    		KM_TYPE_NR
    	};
    

    七、每个CPU的分配

    1.自己创建的方法
    	unsigned long my_percpu[NR_CPUS];
    	int cpu;
    	cpu = get_cpu();		//获得当前处理器,并禁止内核抢占
    	my_percpu[cpu]++;		//对每个CPU的数据进行处理
    	put_cpu();			//激活内核抢占
    2.新的每个CPU接口
    编译时的每个CPU数据
    DEFINE_PER_CPU(type, name);			//定义每个CPU变量
    DECLARE_PER_CPU(type, name);			//在别处声明变量,以供其他地方使用。
    
    get_cpu_var(name)++;		//返回当前处理器上的指定变量,同时禁止抢占;并对其执行++ 操作
    put_cpu_var(name);		//重新激活抢占
    
    per_cpu(name, cpu) ++;	//增加指定处理器上的name变量的值
    
    运行时的每个CPU数据
    void * alloc_percpu(type);
    void * __alloc_percpu(size_t size, size_t align);
    void free_percpu(const void * );
    
    例如:
    struct rabid_cheetah ptr = alloc_percpu(struct rabid_cheetah);
    等价于
    struct rabid_cheetah ptr = __alloc_percpu(sizeof(struct rabid_cheetah),
    				__alignof__(struct rabid_cheetah));
    																			
    __alignof__是gcc的一个功能,它会返回指定类型所需的对齐字节数。它的语义和sizeof一样。
    
    get_cpu_var(ptr);		返回了一个指向当前处理器数据的特殊实例,同时禁止内核抢占。
    put_cpu_var(ptr);		重新激活内核抢占。
    
    使用例子:
    void *percpu_ptr;
    unsigned long * foo;
    
    percpu_ptr = alloc_percpu(unsigned long);
    if(!percpu_ptr)
    		/*内存分配错误*/
    foo = get_cpu_var(percpu_ptr);
    /*操作foo*/
    put_cpu_var(percpu_ptr);
    free_precpu(percpu_ptr);
    

      

  • 相关阅读:
    Ngnix(三)—— window下布置nginx服务集群
    Java基础(一)—— 网络编程(一)—— Java Socket总结
    2018新浪Java笔试总结
    java yyyyMMddHHmmss格式字符串转换为yyyy-MM-dd HH:mm:ss格式字符串
    c# 返回多个参数(利用Tuple)
    c# 域名转换成ip地址
    myhaits if test判断字符串
    java中List转换成Json
    java打包发布程序.jar(Eclipse)
    redis设置密码
  • 原文地址:https://www.cnblogs.com/hei-da-mi/p/6003422.html
Copyright © 2011-2022 走看看