内存管理
页
内核把物理页作为内存管理的基本单位;内存管理单元(MMU,管理内存并把虚拟地址转换为物理地址)通常以页为单位进行处理。MMU以页大小为单位来管理系统中的页表。从虚拟内存的角度看,页就是最小单位。32位系统:页大小4KB64位系统:页大小8KB在支持4KB页大小并有1GB物理内存的机器上,物理内存会被划分为262144个页。内核用 struct page 结构表示系统中的每个物理页。struct page {page_flags_t flags; /* 表示页的状态,每一位表示一种状态*/atomic_t _count; /* 存放页的引用计数,0代表没有被引用 */atomic_t _mapcount;unsigned long private;strcut address_space *mapping;pgoff_t index;struct list_head lru;void *virtual; /* 页在虚拟内存中的地址,动态映射物理页 */}下面,我们来解释下其中的重要字段。flags:这个字段用于存放页的状态。这些状态包括页是不是脏的,是不是被锁定在内存中等。 flag 的每一位单独表示一种状态,所以,它至少可以同时表示出32种不同的状态。_count:这个字段存放页的使用计数,也就是这个页被引用了多少次。很奇怪,技术值变为 -1 时,就说明当前内核并没有引用这一页,于是,在新的分配中就可以使用它,注意,这个字段使用的是 -1 代表未使用,而不是 0 。virtual:这个字段是页的虚拟地址。mapping:这个域指向和这个页关联的address_space 对象。private:这个根据名字就可以看得出,它指向私有数据。内核通过这样的数据结构管理系统中所有的页,因为内核需要知道一个页是否空闲,谁有拥有这个页。拥有者可能是:用户空间进程、动态分配的内核数据、静态内核代码、页高速缓存等等。系统中每一个物理页都要分配这样一个结构体,进行内存管理。Linux基础篇之内存管理机制 http://www.linuxidc.com/Linux/2014-03/98293.htm
Linux内存管理之高端内存 http://www.linuxidc.com/Linux/2013-06/85693.htm
Linux内存管理之分段机制 http://www.linuxidc.com/Linux/2012-11/74480.htm
Linux内存管理伙伴算法 http://www.linuxidc.com/Linux/2012-09/70711.htm
区
由于硬件的限制,内核并不能对所有的页一视同仁。Linux必须处理如下两种由于硬件存在缺陷而引起的内存寻址问题:1)一些硬件只能用某些特定的内存地址来执行DMA(直接内存访问)。2)一些体系结构其内存的物理寻址范围比虚拟寻址范围大得多。这样,就有一些内存不能永久地映射到内核空间上。由于存在这种限制,内核把具有相似特性的页划分为不同的区(ZONE):1)ZONE_DMA——这个区包含的页能用来执行DMA操作。2)ZONE_NORMAL——这个区包含的都是能正常地映射网页。3)ZONE_DMA32——同上,不过只能被32位设备访问4)ZONE_HIGHMEM——这个区包含“高端内存”,其中的页并能不永久地映射到内核地址空间。Linux把系统的页划分为区,形成不同的内存池,这样就可以根据用途进行分配。注意,区的划分没有任何物理意义,这只是内核为了管理页而采取的一种逻辑上的分组。用于DMA的内存必须从ZONE_DMA中进行分配,但是一般用途的内存却既能从ZONE_DMA分配,也能从ZONE_NORMAL分配。
获得页
内核提供了一种请求内存的底层机制,并提供了对它进行访问的几个接口。所有这些接口都以页为单位分配内存,定义于<linux/gfp.h>。最核心的函数是:structpage *alloc_pages( unsigned int gfp_mask, unsigned int order );该函数分配 2order 个连续的物理页,并返回一个指向第一页的 page 结构体指针,如果出错就返回NULL。void*page_address( struct page *page );把给定的页转换成它的逻辑地址。如果无须用到 struct page,可以调用:unsignedlong __get_free_pages( unsigned int gfp_mask, unsigned int order );这个函数与alloc_pages 作用相同,不过它直接返回所请求的第一个页的逻辑地址。因为页是连续的,因此其他页也会紧随其后。如果只需要一页,可以用以下两个函数:structpage *alloc_page( unsigned int gfp_mask );unsignedlong _get_free_page( unsigned int gfp_mask );如果需要让返回页的内容全为0,可以使用下面这个函数unsignedlong get_zeroed_page(unsigned int gfp_mask );
| 方法 | 描述 |
| alloc_page(gfp_mask) | 只分配一页,返回指向页结构的指针 |
| alloc_pages(gfp_mask, order) | 分配 2^order 个页,返回指向第一页页结构的指针 |
| __get_free_page(gfp_mask) | 只分配一页,返回指向其逻辑地址的指针 |
| __get_free_pages(gfp_mask, order) | 分配 2^order 个页,返回指向第一页逻辑地址的指针 |
| get_zeroed_page(gfp_mask) | 只分配一页,让其内容填充为0,返回指向其逻辑地址的指针 |
当不再需要页时可以使用以下函数来释放它。void__free_pages( struct page *page, unsigned int order );voidfree_pages( unsigned long addr, unsigned int order );voidfree_page( unsigned long addr );释放页时要谨慎,只能释放属于你的页。传递了错误的 struct page 或地址,用了错误的 order 值都可能导致系统崩溃。请记住,内核是完全依赖自己的。
更多详情见请继续阅读下一页的精彩内容: http://www.linuxidc.com/Linux/2014-08/105365p2.htm