精华内容
下载资源
问答
  • 详细介绍了linux内核中用到的内存操作相关函数,如内存申请、释放、读写等函数接口,很全面,推荐大家收藏。
  • Linux内核中常见内存分配函数 1. 原理说明 Linux内核中采用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系统中,用到了四级页表,如图2-1所示。四级页表分别为...

    Linux内核中常见内存分配函数

    1.    原理说明

    Linux内核中采用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系统中,用到了四级页表,如图2-1所示。四级页表分别为:

        页全局目录(Page Global Directory)

        页上级目录(Page Upper Directory)

        页中间目录(Page Middle Directory)

        页表(Page Table)

        页全局目录包含若干页上级目录的地址,页上级目录又依次包含若干页中间目录的地址,而页中间目录又包含若干页表的地址,每一个页表项指向一个页框。Linux中采用4KB大小的页框作为标准的内存分配单元。

     

    多级分页目录结构

    1.1.    伙伴系统算法

        在实际应用中,经常需要分配一组连续的页框,而频繁地申请和释放不同大小的连续页框,必然导致在已分配页框的内存块中分散了许多小块的空闲页框。这样,即使这些页框是空闲的,其他需要分配连续页框的应用也很难得到满足。

        为了避免出现这种情况,Linux内核中引入了伙伴系统算法(buddy system)。把所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。最大可以申请1024个连续页框,对应4MB大小的连续内存。每个页框块的第一个页框的物理地址是该块大小的整数倍。

        假设要申请一个256个页框的块,先从256个页框的链表中查找空闲块,如果没有,就去512个页框的链表中找,找到了则将页框块分为2个256个页框的块,一个分配给应用,另外一个移到256个页框的链表中。如果512个页框的链表中仍没有空闲块,继续向1024个页框的链表查找,如果仍然没有,则返回错误。

        页框块在释放时,会主动将两个连续的页框块合并为一个较大的页框块。

    1.2.    slab分配器

        slab分配器源于 Solaris 2.4 的分配算法,工作于物理内存页框分配器之上,管理特定大小对象的缓存,进行快速而高效的内存分配。

        slab分配器为每种使用的内核对象建立单独的缓冲区。Linux 内核已经采用了伙伴系统管理物理内存页框,因此 slab分配器直接工作于伙伴系统之上。每种缓冲区由多个 slab 组成,每个 slab就是一组连续的物理内存页框,被划分成了固定数目的对象。根据对象大小的不同,缺省情况下一个 slab 最多可以由 1024个页框构成。出于对齐等其它方面的要求,slab 中分配给对象的内存可能大于用户要求的对象实际大小,这会造成一定的内存浪费。

    2.    常用内存分配函数

    2.1.    __get_free_pages

        unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)

     

        __get_free_pages函数是最原始的内存分配方式,直接从伙伴系统中获取原始页框,返回值为第一个页框的起始地址。__get_free_pages在实现上只是封装了alloc_pages函数,从代码分析,alloc_pages函数会分配长度为1<<order的连续页框块。order参数的最大值由include/linux/Mmzone.h文件中的MAX_ORDER宏决定,在默认的2.6.18内核版本中,该宏定义为10。也就是说在理论上__get_free_pages函数一次最多能申请1<<10 * 4KB也就是4MB的连续物理内存。但是在实际应用中,很可能因为不存在这么大量的连续空闲页框而导致分配失败。在测试中,order为10时分配成功,order为11则返回错误。

    2.2.    kmem_cache_alloc

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

        void *kmem_cache_alloc(struct kmem_cache *c, gfp_t flags)

     

        kmem_cache_create/ kmem_cache_alloc是基于slab分配器的一种内存分配方式,适用于反复分配释放同一大小内存块的场合。首先用kmem_cache_create创建一个高速缓存区域,然后用kmem_cache_alloc从该高速缓存区域中获取新的内存块。 kmem_cache_alloc一次能分配的最大内存由mm/slab.c文件中的MAX_OBJ_ORDER宏定义,在默认的2.6.18内核版本中,该宏定义为5,于是一次最多能申请1<<5 * 4KB也就是128KB的连续物理内存。分析内核源码发现,kmem_cache_create函数的size参数大于128KB时会调用BUG()。测试结果验证了分析结果,用kmem_cache_create分配超过128KB的内存时使内核崩溃。

    2.3.    kmalloc

        void *kmalloc(size_t size, gfp_t flags)

     

        kmalloc是内核中最常用的一种内存分配方式,它通过调用kmem_cache_alloc函数来实现。kmalloc一次最多能申请的内存大小由include/linux/Kmalloc_size.h的内容来决定,在默认的2.6.18内核版本中,kmalloc一次最多能申请大小为131702B也就是128KB字节的连续物理内存。测试结果表明,如果试图用kmalloc函数分配大于128KB的内存,编译不能通过。

    2.4.    vmalloc

        void *vmalloc(unsigned long size)

     

        前面几种内存分配方式都是物理连续的,能保证较低的平均访问时间。但是在某些场合中,对内存区的请求不是很频繁,较高的内存访问时间也可以接受,这是就可以分配一段线性连续,物理不连续的地址,带来的好处是一次可以分配较大块的内存。图3-1表示的是vmalloc分配的内存使用的地址范围。vmalloc对一次能分配的内存大小没有明确限制。出于性能考虑,应谨慎使用vmalloc函数。在测试过程中,最大能一次分配1GB的空间。

     

    Linux内核部分内存分布

    2.5.    dma_alloc_coherent

        void *dma_alloc_coherent(struct device *dev, size_t size, ma_addr_t *dma_handle, gfp_t gfp)

        DMA是一种硬件机制,允许外围设备和主存之间直接传输IO数据,而不需要CPU的参与,使用DMA机制能大幅提高与设备通信的吞吐量。DMA操作中,涉及到CPU高速缓存和对应的内存数据一致性的问题,必须保证两者的数据一致,在x86_64体系结构中,硬件已经很好的解决了这个问题, dma_alloc_coherent和__get_free_pages函数实现差别不大,前者实际是调用__alloc_pages函数来分配内存,因此一次分配内存的大小限制和后者一样。__get_free_pages分配的内存同样可以用于DMA操作。测试结果证明,dma_alloc_coherent函数一次能分配的最大内存也为4M

    2.6.    ioremap

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

        ioremap是一种更直接的内存“分配”方式,使用时直接指定物理起始地址和需要分配内存的大小,然后将该段物理地址映射到内核地址空间。ioremap用到的物理地址空间都是事先确定的,和上面的几种内存分配方式并不太一样,并不是分配一段新的物理内存ioremap多用于设备驱动,可以让CPU直接访问外部设备的IO空间。ioremap能映射的内存由原有的物理内存空间决定,所以没有进行测试。

    2.7. Boot Memory

        如果要分配大量的连续物理内存,上述的分配函数都不能满足,就只能用比较特殊的方式,在Linux内核引导阶段来预留部分内存。

    2.7.1.  在内核引导时分配内存

        void* alloc_bootmem(unsigned long size)

        可以在Linux内核引导过程中绕过伙伴系统来分配大块内存。使用方法是在Linux内核引导时,调用mem_init函数之前用alloc_bootmem函数申请指定大小的内存。如果需要在其他地方调用这块内存,可以将alloc_bootmem返回的内存首地址通过EXPORT_SYMBOL导出,然后就可以使用这块内存了。这种内存分配方式的缺点是,申请内存的代码必须在链接到内核中的代码里才能使用,因此必须重新编译内核,而且内存管理系统看不到这部分内存,需要用户自行管理。测试结果表明,重新编译内核后重启,能够访问引导时分配的内存块。

    2.7.2. 通过内核引导参数预留顶部内存

        在Linux内核引导时,传入参数“mem=size”保留顶部的内存区间。比如系统有256MB内存,参数“mem=248M”会预留顶部的8MB内存,进入系统后可以调用ioremap(0xF800000,0x800000)来申请这段内存。

    3.    几种分配函数的比较

     

    分配原理

    最大内存

    其他

    __get_free_pages

    直接对页框进行操作

    4MB

    适用于分配较大量的连续物理内存

    kmem_cache_alloc

    基于slab机制实现

    128KB

    适合需要频繁申请释放相同大小内存块时使用

    kmalloc

    基于kmem_cache_alloc实现

    128KB

    最常见的分配方式,需要小于页框大小的内存时可以使用

    vmalloc

    建立非连续物理内存到虚拟地址的映射

     

    物理不连续,适合需要大内存,但是对地址连续性没有要求的场合

    dma_alloc_coherent

    基于__alloc_pages实现

    4MB

    适用于DMA操作

    ioremap

    实现已知物理地址到虚拟地址的映射

     

    适用于物理地址已知的场合,如设备驱动

    alloc_bootmem

    在启动kernel时,预留一段内存,内核看不见

     

    小于物理内存大小,内存管理要求较高

       注:表中提到的最大内存数据来自CentOS5.3 x86_64系统,其他系统和体系结构会有不同


    1、kmalloc()/kfree()

      static __always_inline void *kmalloc(size_t size, gfp_tflags)
      内核空间申请指定大小的内存区域,返回内核空间虚拟地址。在函数实现中,如果申请的内存空间较大的话,会从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 intorder)作用相当于alloc_pages(NORMAL)+kmap(),但不能申请高端内存页面。
      __get_free_page()只申请一个页面。

    5、kmap()/kunmap()

      返回指定页面对应内核空间的虚拟地址。
      #include
      void *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)
      #endif

    8、ioremap()/iounmap()

      ioremap()的作用是把device寄存器和内存的物理地址区域映射到内核虚拟区域,返回值为内核的虚拟地址。
      注明:在内核中操作内存空间时使用的都是内核虚拟地址,必须把device的空间映射到内核虚拟空间。
      #include
      void *ioremap(unsigned long phys_addr, unsigned longsize);
      void *ioremap_nocache(unsigned long phys_addr, unsigned longsize); 映射非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 longcount);
      void ioread16_rep(void *addr, void *buf, unsigned longcount);
      void ioread32_rep(void *addr, void *buf, unsigned longcount);
      void iowrite8_rep(void *addr, const void *buf, unsigned longcount);
      void iowrite16_rep(void *addr, const void *buf, unsignedlong count);
      void iowrite32_rep(void *addr, const void *buf, unsignedlong 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)输入的参数依次是设备的物理地址,字节长度,设备名字。函数返回类型如下
      struct resource {
      resource_size_t start;
      resource_size_t end;
      const char *name;
      unsigned long flags;
      struct resource *parent, *sibling, *child;
      };

    10、SetPageReserved()

      随着linux的长时间运行,空闲页面会越来越少,为了防止linux内核进入请求页面的僵局中,Linux内核采用页面回收算法(PFRA)从用户进程和内核高速缓存中回收内存页框,并根据需要把要回收页框的内容交换到磁盘上的交换区。调用该函数可以使页面不被交换。
      #define SetPageReserved(page) set_bit(PG_reserved,&(page)->flags)
      PG_reserved 的标志说明如下。
      * PG_reserved is set for special pages, which can nEVEr beswapped out. Some
      * of them might not EVEn exist (eg empty_bad_page)…

    11、do_mmap()/do_ummap()

      内核使用do_mmap()函数为进程创建一个新的线性地址区间。但是说该函数创建了一个新VMA并不非常准确,因为如果创建的地址区间和一个已经存在的地址区间相邻,并且它们具有相同的访问权限的话,那么两个区间将合并为一个。如果不能合并,那么就确实需要创建一个新的VMA了。但无论哪种情况,do_mmap()函数都会将一个地址区间加入到进程的地址空间中--无论是扩展已存在的内存区域还是创建一个新的区域。
      同样,释放一个内存区域应使用函数do_ummap(),它会销毁对应的内存区域。

    12、get_user_pages()

      作用是在内核空间获取用户空间内存的page 描述,之后可以通过函数kmap() 获取page对应到内核的虚拟地址。
      int get_user_pages(struct task_struct *tsk, struct mm_struct*mm,
      unsigned long start, int len, int write, int force,
      struct page **pages, struct vm_area_struct **vmas)
      参数说明
      参数tsk:指示用户空间对应进程的task_struct数据结构。只是为了记录错误信息用,该参数可以为空。
      参数mm:从该mm struct中获取start 指示的若干页面。
      参数start:参数mm空间的起始地址,即用户空间的虚拟地址。
      参数len:需要映射的页数。
      参数write:可以写标志。
      参数force:强制可以写标志。
      参数pages:输出的页数据结构。
      参数vmas:对应的需要存储区,(没有看明白对应的代码)
      返回值:数返回实际获取的页数,貌似对每个实际获取的页都是给页计数值增1,如果实际获取的页不等于请求的页,要放弃操作则必须对已获取的页计数值减1.

    13、copy_from_user()和copy_to_user()

      主要应用于设备驱动中读写函数中,通过系统调用触发,在当前进程上下文内核态运行(即当前进程通过系统调用触发)。
      copy_from_user的目的是防止用户程序欺骗内核,将一个非法的地址传进去,如果没有它,这一非法地址就检测不到,内和就会访问这个地址指向的数据。因为在内核中访问任何地址都没有保护,如果不幸访问一个错误的内存地址会搞死内核或发生更严重的问题
      copy_from_user调用了access_ok,所以才有"自己判断功能"
      access_ok(),可以检查访问的空间是否合法。
      注意:中断代码时不能用copy_from_user,因为其调用了might_sleep()函数,会导致睡眠

      unsigned long copy_to_user(void __user *to, const void*from, unsigned long n)
      通常用在设备读函数或ioctl中获取参数的函数中:其中"to"是用户空间的buffer地址,在本函数中将内核buffer"from"除的n个字节拷贝到用户空间的"to"buffer.

      unsigned long copy_from_user(void *to, const void __user*from, unsigned long n)
      通常用在设备写函数或ioctl中设置参数的函数中:"to"是内核空间的buffer指针,要写入的buffer;"from"是用户空间的指针,数据源buffer.

    14、get_user(x, ptr)

      本函数的作用是获取用户空间指定地址的数值并保存到内核变量x中,ptr为用户空间的地址。用法举例如下。
      get_user(val, (int __user *)arg)
      注明:函数用户进程上下文内核态,即通常在系统调用函数中使用该函数。

    15、put_user(x, ptr)

      本函数的作用是将内核空间的变量x的数值保存到用户空间指定地址处,prt为用户空间地址。用法举例如下。
      put_user(val, (int __user *)arg)
      注明:函数用户进程上下文内核态,即通常在系统调用函数中使用该函数

    展开全文
  • 一、start_kernel启动调用 ...e820处理相关函数,如e820_add_kernel_range()。主要是e820探测物理内存,添加到内核。 max_pfn\max_low_pfn。根据e820探测的物理内存,确定这两个全局变量,用来确定各个zone范围。 me

    一、start_kernel启动调用

    start_kernel->

    • setup_arch()
    • build_all_zonelists()
    • mm_init()
    • setup_per_cpu_pageset()

    以下各函数简介。

    二、setup_arch()

    • e820处理相关函数,如e820_add_kernel_range()。主要是e820探测物理内存,添加到内核。
    • max_pfn\max_low_pfn。根据e820探测的物理内存,确定这两个全局变量,用来确定各个zone范围。
    • memblock_x86_fill()。初始化memblock内存预分配系统,两种type,memory和reserved。
    • conplat_reserve_memory()。私有的大块内存预分配处理,利用memblock预留出大块内存,供cblock来管理。cblock用于内核启动后可以分配出大块内存。
    • x86_init.paging.pagetable_init()=xen_pagetable_init->paging_init()->zone_size_init()。初始化各zone的范围,并且初始化zone的基本信息,如一些锁、span(present\managed)_pages等。这里确定了dma32和normalzone的范围。

    三、build_all_zonelists()

    这个函数将各个node的所有zone串接起来。如果local node内存不足,根据此表找到其他node申请内存。前面文章有介绍。

    四、mm_init()

    • mem_init()。该函数调用一个很重更要的函数free_all_bootmem()。free_all_bootmem()将现在memblock管理的内存memory并且非reserved的内存释放出来,将reserved的帧__SetPageReserved,将剩余的可用的帧调用__ClearPageReserved,这样伙伴系统在初始化时可以使用这些free的帧。还有一点,那么非reserved的帧会被统计起来,totalram_pages+= 非reserved的帧,该totalram_pages变量/proc统计会用。
    展开全文
  • 1、kmalloc()/kfree() ...在函数实现中,如果申请的内存空间较大的话,会从buddy系统申请若干内存页面,如果申请的内存空间大小较小的话,会从slab系统中申请内存空间。有关buddy和slab,请参见

    1kmalloc()/kfree()

    static __always_inline void *kmalloc(size_t size, gfp_t flags)

    内核空间申请指定大小的内存区域,返回内核空间虚拟地址。在函数实现中,如果申请的内存空间较大的话,会从buddy系统申请若干内存页面,如果申请的内存空间大小较小的话,会从slab系统中申请内存空间。有关buddyslab,请参见《linux内核之内存管理.doc

    gfp_t flags 的选项较多。参考内核文件gfp.h

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

     

     

     

     

    2vmalloc()/vfree()

    void *vmalloc(unsigned long size)

    函数作用:从高端(如果存在,优先从高端)申请内存页面,并把申请的内存页面映射到内核的动态映射空间。vmalloc()函数的功能和alloc_pages(_GFP_HIGHMEM)+kmap() 的功能相似,只所以说是相似而不是相同,原因在于用vmalloc()申请的物理内存页面映射到内核的动态映射区(见下图),并且,用vmalloc()申请的页面的物理地址可能是不连续的。而alloc_pages(_GFP_HIGHMEM)+kmap()申请的页面的物理地址是连续的,被映射到内核的KMAP区。

    vmalloc分配的地址则限于vmalloc_startvmalloc_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()只申请一个页面。

     

     

    5kmap()/kunmap()

    返回指定页面对应内核空间的虚拟地址。

    #include <linux/highmem.h>

    void *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)

    #endif

     

    8ioremap()/iounmap()

    ioremap()的作用是把device寄存器和内存的物理地址区域映射到内核虚拟区域,返回值为内核的虚拟地址。

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

    #include <asm/io.h>

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

    void *ioremap_nocache(unsigned long phys_addr, unsigned long size);  映射非cacheio内存区域

    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 开始.

     

    9request_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) 输入的参数依次是设备的物理地址,字节长度,设备名字。函数返回类型如下

    struct resource {

           resource_size_t start;

           resource_size_t end;

           const char *name;

           unsigned long flags;

           struct resource *parent, *sibling, *child;

    };

     

     

    10、SetPageReserved()

    随着linux的长时间运行,空闲页面会越来越少,为了防止linux内核进入请求页面的僵局中,Linux内核采用页面回收算法(PFRA)从用户进程和内核高速缓存中回收内存页框,并根据需要把要回收页框的内容交换到磁盘上的交换区。调用该函数可以使页面不被交换。

    #define SetPageReserved(page)                set_bit(PG_reserved, &(page)->flags)

    PG_reserved 的标志说明如下。

    * PG_reserved is set for special pages, which can never be swapped out. Some

     * of them might not even exist (eg empty_bad_page)...

    可参考下面的文章

    http://blog.csdn.net/bullbat/article/details/7311205

    http://blog.csdn.net/cxylaf/article/details/1626534

     

     

    11do_mmap()/do_ummap()

    内核使用do_mmap()函数为进程创建一个新的线性地址区间。但是说该函数创建了一个新VMA并不非常准确,因为如果创建的地址区间和一个已经存在的地址区间相邻,并且它们具有相同的访问权限的话,那么两个区间将合并为一个。如果不能合并,那么就确实需要创建一个新的VMA了。但无论哪种情况, do_mmap()函数都会将一个地址区间加入到进程的地址空间中--无论是扩展已存在的内存区域还是创建一个新的区域。

    同样,释放一个内存区域应使用函数do_ummap(),它会销毁对应的内存区域。

     

     

     

    12get_user_pages()

    作用是在内核空间获取用户空间内存的page 描述,之后可以通过函数kmap() 获取page 对应到内核的虚拟地址

    int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, 
    unsigned long start, int len, int write, int force, 
    struct page **pages, struct vm_area_struct **vmas)

    参数说明

    参数tsk:指示用户空间对应进程的task_struct数据结构。只是为了记录错误信息用,该参数可以为空。

    参数mm:从该mm struct中获取start 指示的若干页面。

    参数start:参数mm空间的起始地址,即用户空间的虚拟地址。

    参数len:需要映射的页数。

    参数write:可以写标志。

    参数force:强制可以写标志。

    参数pages:输出的页数据结构。

    参数vmas:对应的需要存储区,(没有看明白对应的代码)

    返回值:数返回实际获取的页数,貌似对每个实际获取的页都是给页计数值增1,如果实际获取的页不等于请求的页,要放弃操作则必须对已获取的页计数值减1

     

     

    13copy_from_user()copy_to_user()

    主要应用于设备驱动中读写函数中,通过系统调用触发,在当前进程上下文内核态运行(即当前进程通过系统调用触发)。

    copy_from_user的目的是防止用户程序欺骗内核,将一个非法的地址传进去,如果没有它,这一非法地址就检测不到,内和就会访问这个地址指向的数据。因为在内核中访问任何地址都没有保护,如果不幸访问一个错误的内存地址会搞死内核或发生更严重的问题

    copy_from_user调用了access_ok,所以才有自己判断功能

    access_ok(),可以检查访问的空间是否合法。

    注意:中断代码时不能用copy_from_user,因为其调用了might_sleep()函数,会导致睡眠。

     

    unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)

    通常用在设备读函数或ioctl 中获取参数的函数中:其中“to”是用户空间的buffer地址,在本函数中将内核bufferfrom”除的n个字节拷贝到用户空间的“tobuffer

     

    unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)

    通常用在设备写函数或ioctl中设置参数的函数中:to”是内核空间的buffer指针,要写入的buffer;“from”是用户空间的指针,数据源buffer

     

     

     

    14get_user(x, ptr)

    本函数的作用是获取用户空间指定地址的数值并保存到内核变量x中,ptr为用户空间的地址。用法举例如下。

    get_user(val, (int __user *)arg)

    注明:函数用户进程上下文内核态,即通常在系统调用函数中使用该函数。

     

    15put_user(x, ptr)

    本函数的作用是将内核空间的变量x的数值保存到用户空间指定地址处,prt为用户空间地址。用法举例如下。

    put_user(val, (int __user *)arg)

    注明:函数用户进程上下文内核态,即通常在系统调用函数中使用该函数。

    展开全文
  • linux内核内存相关的操作函数     1、kmalloc()/kfree() static __always_inline void *kmalloc(size_t size, gfp_t flags) 内核空间申请指定大小的内存区域,返回内核空间虚拟地址。在函数实现中,如果...

     

    linux内核中内存相关的操作函数

     

     

    1、kmalloc()/kfree()

    static __always_inline void *kmalloc(size_t size, gfp_t flags)

    内核空间申请指定大小的内存区域,返回内核空间虚拟地址。在函数实现中,如果申请的内存空间较大的话,会从buddy系统申请若干内存页面,如果申请的内存空间大小较小的话,会从slab系统中申请内存空间。有关buddy和slab,请参见《linux内核之内存管理.doc》

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

    返回指定页面对应内核空间的虚拟地址。

    #include <linux/highmem.h>

    void *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)

    #endif

     

    8、ioremap()/iounmap()

    ioremap()的作用是把device寄存器和内存的物理地址区域映射到内核虚拟区域,返回值为内核的虚拟地址。

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

    #include <asm/io.h>

    void *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) 输入的参数依次是设备的物理地址,字节长度,设备名字。函数返回类型如下

    struct resource {

           resource_size_t start;

           resource_size_t end;

           const char *name;

           unsigned long flags;

           struct resource *parent, *sibling, *child;

    };

     

    10、SetPageReserved()

    随着linux的长时间运行,空闲页面会越来越少,为了防止linux内核进入请求页面的僵局中,Linux内核采用页面回收算法(PFRA)从用户进程和内核高速缓存中回收内存页框,并根据需要把要回收页框的内容交换到磁盘上的交换区。调用该函数可以使页面不被交换。

    #define SetPageReserved(page)                set_bit(PG_reserved, &(page)->flags)

    PG_reserved 的标志说明如下。

    * PG_reserved is set for special pages, which can never be swapped out. Some

     * of them might not even exist (eg empty_bad_page)...

    可参考下面的文章

    http://blog.csdn.net/bullbat/article/details/7311205

    http://blog.csdn.net/cxylaf/article/details/1626534

     

    11、do_mmap()/do_ummap()

    内核使用do_mmap()函数为进程创建一个新的线性地址区间。但是说该函数创建了一个新VMA并不非常准确,因为如果创建的地址区间和一个已经存在的地址区间相邻,并且它们具有相同的访问权限的话,那么两个区间将合并为一个。如果不能合并,那么就确实需要创建一个新的VMA了。但无论哪种情况, do_mmap()函数都会将一个地址区间加入到进程的地址空间中--无论是扩展已存在的内存区域还是创建一个新的区域。

    同样,释放一个内存区域应使用函数do_ummap(),它会销毁对应的内存区域。

     

    12、get_user_pages()

    作用是在内核空间获取用户空间内存的page 描述,之后可以通过函数kmap() 获取page 对应到内核的虚拟地址。

    int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
    unsigned long start, int len, int write, int force,
    struct page **pages, struct vm_area_struct **vmas)

    参数说明

    参数tsk:指示用户空间对应进程的task_struct数据结构。只是为了记录错误信息用,该参数可以为空。

    参数mm:从该mm struct中获取start 指示的若干页面。

    参数start:参数mm空间的起始地址,即用户空间的虚拟地址。

    参数len:需要映射的页数。

    参数write:可以写标志。

    参数force:强制可以写标志。

    参数pages:输出的页数据结构。

    参数vmas:对应的需要存储区,(没有看明白对应的代码)

    返回值:数返回实际获取的页数,貌似对每个实际获取的页都是给页计数值增1,如果实际获取的页不等于请求的页,要放弃操作则必须对已获取的页计数值减1。

     

    13、copy_from_user()和copy_to_user()

    主要应用于设备驱动中读写函数中,通过系统调用触发,在当前进程上下文内核态运行(即当前进程通过系统调用触发)。

    copy_from_user的目的是防止用户程序欺骗内核,将一个非法的地址传进去,如果没有它,这一非法地址就检测不到,内和就会访问这个地址指向的数据。因为在内核中访问任何地址都没有保护,如果不幸访问一个错误的内存地址会搞死内核或发生更严重的问题

    copy_from_user调用了access_ok,所以才有“自己判断功能“

    access_ok(),可以检查访问的空间是否合法。

    注意:中断代码时不能用copy_from_user,因为其调用了might_sleep()函数,会导致睡眠。

     

    unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)

    通常用在设备读函数或ioctl 中获取参数的函数中:其中“to”是用户空间的buffer地址,在本函数中将内核buffer“from”除的n个字节拷贝到用户空间的“to”buffer。

     

    unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)

    通常用在设备写函数或ioctl中设置参数的函数中:“to”是内核空间的buffer指针,要写入的buffer;“from”是用户空间的指针,数据源buffer。

     

    14、get_user(x, ptr)

    本函数的作用是获取用户空间指定地址的数值并保存到内核变量x中,ptr为用户空间的地址。用法举例如下。

    get_user(val, (int __user *)arg)

    注明:函数用户进程上下文内核态,即通常在系统调用函数中使用该函数。

     

    15、put_user(x, ptr)

    本函数的作用是将内核空间的变量x的数值保存到用户空间指定地址处,prt为用户空间地址。用法举例如下。

    put_user(val, (int __user *)arg)

    注明:函数用户进程上下文内核态,即通常在系统调用函数中使用该函数。

     

    展开全文
  •  1、kmalloc()/kfree()  static __always_inline void ...在函数实现中,如果申请的内存空间较大的话,会从buddy系统申请若干内存页面,如果申请的内存空间大小较小的话,会从slab系统中申请内存空间。  gfp_t
  •  Linux内核中采 用了一种同时适用于32位和64位系统的内 存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表,如图2-1所示。四级页表分别为:  * 页全局目录(Page Global ...
  • 做过C开发的同学都比较清楚,内存分配是咱们整个开发中非常关键的一个操作,在用户态,用的最多的内存分配函数当然是malloc(),其对应的释放函数free(),通过这对函数即可实现用户态的内存相关的操作 那么,在...
  • 内核函数相关区别

    2019-12-04 16:56:28
    mmap 将硬件物理地址映射到...在内核驱动中如果要访问设备的物理地址,需要利用ioremap将设备的物理地址映射到内核虚拟地址上(动态内存映射区),以后驱动程序访问这个内核虚拟地址就是在间接得访问设备的物理地 ...
  • Linux内存分配相关函数详解 kmalloc()、kzalloc()、vmalloc(). 动态申请内存 MMU 操作系统中的内存管理 虚拟内存机制 页式内存管理 内核虚拟内存布局 kmalloc()与kfree() kzalloc() vmalloc()、vfree() 总结...
  • percpu_drift_mark伙伴系统相关 知识点大杂烩 zone->percpu_drift_mark 该值初始化函数:mm/page_alloc.c:init_per_zone_wmark_min->refresh_zone_stat_thresholds() 常规条件下zone区域的free_pages获取...
  • 内存映射相关函数

    千次阅读 2013-04-07 12:22:57
    内存映射相关函数 本文介绍利用内存映射文件修改大文件:在大文件内存前加入一段数据,若要使用内存映射文件,必须执行下列操作步骤: 创建或打开一个文件内核对象,该对象用于标识磁盘上你想用作内存映射...
  • http://blog.csdn.net/gxfan/article/details/2723455 kmalloc vmalloc和kmap的区别以及下一篇关于内存布局 http://blog.csdn.net/xiaojsj111/article/details/1...
  • CreateFile:创建或者打开文件CreateFileMapping:创建文件映射内核对象MapViewOfFile:保留一块地址空间区域,将文件数据作为物理存储提交到该空间UnmapViewOfFile:取消文件映射FlushViewOfFile:把修改过的数据...
  • 今天在用内存模拟32MB u盘让PC识别。涉及到通信的字节序问题。 看到如下函数 put_unaligned_be32(data,&buf) 向这类函数。就是得到或是写入相应字节序的数据。 put写入 get获得 be大端 le小端
  • 据我离职转行嵌入式已经有一段时间了,真是学到深处代码难,理解了为啥很少有人去搞内核,也不是没有原因; 现在承受着多种压力的人们,生活节奏页也逐渐加快,有段可以自我提升的时间简直就是奢望,我是多么的奢侈...
  • Linux内存管理的相关函数,在此做个笔记,供以后需要时查阅。 1 内核空间动态申请 主要包括 kmalloc(), __get_free_page() 系列:申请的内存位于物理内存映射区,而且在物理上也连续,与真实的物理地址也只有一个...
  • 内核函数pages相关

    千次阅读 2018-07-24 19:16:35
    void *alloc_pages_exact(size_t size, gfp_t gfp_mask)功能:类似于__get_free_pages其实它内部也是调用了__get_free_pages,不同之处是__get_free_pages会申请2^order个物理页,但此函数申请满足需求的最小页个数 ...
  • brk和sbrk主要的工作是实现虚拟内存内存的映射.在GNUC中,内存分配是这样的...如果这块空间不够,malloc函数族(realloc,calloc等)就调用sbrk函数将数据段的下界移动,sbrk函数内核的管理 下将虚拟地址空间映射到...
  • 内存管理器(九)内核内存管理–进程相关数据结构 前言 前面的几篇博文都是在学习用户态内存管理,malloc与free 也研究了Glibc 的内存管理函数的实现,本来想点到位置,但是觉得还是太浅了,不够深入,遂决定...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 800
精华内容 320
关键字:

内核内存相关函数