【linux】内存管理基础(1)

内存管理

页(page)

内核中把物理页作为内存管理的基本单位,一般每个页的大小为4KB,例如在有1G的物理内存的机器上,物理内存会被划分为266144个页。内核中用struct page结构表示每个物理页,该结构体位于<linux/mm.types.h>中,事实上,最新Linux版本的struct page实现中大量用到union,也就是同一个元素在不同场景下有不同的意义。因为每个page frame都需要一个struct page来描述,一个Page frame占4KB,一个struct page占32字节,那所有的struct page需要消耗的内存占整个系统内存的32/4096,这一开销占30多MB。struct page描述和管理的是这4KB的物理内存,它并不关注这段内存中的数据变化。

struct page {
    unsigned long flags;//存放页的状态:是否为脏页、是否被锁在内存中
    atomic_t count; //页的引用计数,当为-1时说明内核没有引用该页,-1时使用page_count()返回0,当返                        回count值为0时,该page frame可被free掉
    atomic_t _mapcount;// 表示该page frame被映射的个数,也就是多少个page table entry中含有这个page frame的PFN(页面框架号(pfn))
    struct list_head lru;//"least recently used"的缩写,根据page frame的活跃程度(使用频率),                            一个可回收的page frame要么挂在active_list双向链表上,要么挂在     inactive_list双向链表上,以作为页面回收的选择依据,lru中包含的就是指向所在链表中前后节点的指针
    struct address_space *mapping;//指向文件inode对应的address_space
    unsigned long index;//index表示该page在文件内的offset(以page size为单位)     
    void        *virtual; //页的虚拟地址
    ...  
} 

重要的问题**:那如何找一个page对应的物理地址呢?**(即如何实现每一个page页与物理页一一对应)

我们知道物理内存按照大小为 (1<<PAGE_SHIFT)分为很多个页,每个这样的页就对应一个struct page * page结构,这些页描述结构存放在一个称之为mem_map的数组里面,而且是严格按照物理内存的顺序来存放的,也就是物理上的第一个页描述结构,作为 mem_map数组的第一个元素,依次类推。所以,每个页描述结构(page)在数组mem_map里的位置在乘以页的大小,就可以得到该页的物理地址了。

page和物理页帧转换

define pfn_to_page(pfn)	(mem_map + ((pfn) - PHYS_PFN_OFFSET))//pfn相当于该page在mem_map数组中的索引号,相当于mem_map[pfn]

define page_to_pfn(page)	((unsigned long)((page) - mem_map) + PHYS_PFN_OFFSET)//page-mem_map得到该数组的索引,索引号就是物理页帧号pfn

页的分配与释放

内核提供了一种请求内存的底层机制,并提供了对它进行访问的几个接口。所以这些均以页为单位分配内存,定义于<linux/gfp.h>中

stalloc_page *alloc_page(gfp_t gfp_mask, unsigned int order)

该函数分配2^order(1<<order)个连续的物理页

返回一个指针,该指针指向于第一个页的page结构体

void * page_address(struct page *page)

该函数返回一个指针,指向给定物理页当前所在的逻辑地址。

unsigned long __get_free_pages(gfp_t gfp_mask,unsigned int order)

该函数与alloc_pages()作用相同,不过它直接返回所请求的第一个页的逻辑地址。

区(zone)

对区(zone)的划分于硬件有关,对于不同的处理框架可能不一样。Zone是用于管理物理内存的,但zone与zone之间并没有任何的物理分割,它只是Linux为了便于管理进行的一种逻辑意义上的划分。可以通过"cat /proc/zoneinfo | grep Node"命令查看系统中包含的zones的种类

在这里插入图片描述

如上图,有防止内存碎片化的ZONE_MOVABLE和支持设备热插拔的ZONE_DEVICE。

zone_type定义在include/linux/mmzone.h(内核版本2.6.34)

enum zone_type
{
#ifdef CONFIG_ZONE_DMA
    ZONE_DMA,
#endif

#ifdef CONFIG_ZONE_DMA32

    ZONE_DMA32,
#endif

    ZONE_NORMAL,

#ifdef CONFIG_HIGHMEM
    ZONE_HIGHMEM,
#endif
    ZONE_MOVABLE,
#ifdef CONFIG_ZONE_DEVICE
    ZONE_DEVICE,
#endif
    __MAX_NR_ZONES

};

linux主要使用四种区:

  • ZONE_DMA——这个区的页能用来执行DMA操作
  • ZONE_DMA——和ZONE_DMA类似,只不过只能被32位设备访问
  • ZONE_NORMAL——这个区包含的都是能正常映射的页
  • ZONE_HIGHEM——这个区包含“高端内存”
struct zone {
     spinlock_t         lock;//防止并发访问strcut zone

     unsigned long      spanned_pages;//zone含有的总的page frames数目
     unsigned long      present_pages; 
     unsigned long      nr_reserved_highatomic;    
     atomic_long_t      managed_pages;

     struct free_area   free_area[MAX_ORDER];
     unsigned long      _watermark[NR_WMARK];//watermark有min(mininum), low, high三种,可作为启动内存回收的判断标准
     long               lowmem_reserve[MAX_NR_ZONES];
     atomic_long_t      vm_stat[NR_VM_ZONE_STAT_ITEMS];

     unsigned long      zone_start_pfn;//zone的起始物理页面号
     struct pglist_data *zone_pgdat;//指向zone所属的node
     struct page        *zone_mem_map;
     ...    
} 

在X86结构中,linux内核虚拟地址空间划分03G为用户空间,34G为内核空间。内核虚拟空间(3~4G)又划分为三种类型的区:

ZONE_DMA 3G之后起始的16MB

ZONE_NORMAL 16MB~896MB

ZONE_HIGHMEM 896MB ~1G

由于内核的虚拟和物理地址只差一个偏移量:物理地址=逻辑地址-0xC0000000,所以如果1G内核空间完全用来线性映射,显然物理内也只能访问到1G区间,这显然是不合理的。

x86 32 位系统里,Linux 内核地址空间是指虚拟地址从 0xC0000000 开始到 0xFFFFFFFF 为止的高端内存地址空间,总计 1G 的容量, 包括了内核镜像、物理页面表、驱动程序等运行在内核空间 。

内核空间细分区域:

直接映射区

直接映射区Direct Memory Region:从内核空间起始地址开始,最大896M的内核空间地址区间,为直接内存映射区。

直接映射区的896MB的[线性地址]直接与[物理地址]的前896MB进行映射,也就是说线性地址和分配的物理地址都是连续的。内核地址空间的线性地址0XC0000001所对应的物理地址为0x00000001,它们之间相差一个偏移量PAGE_OFFSET=0XC000000

该区域的线性地址和物理地址存在线性转换关系[线性地址=PAGE_OFFSET+物理地址],也用virt_to_phys()函数将内核虚拟空间中的线性地址转换为物理地址。

动态内存映射区

vmalloc Region该区域由内核函数vmalloc来分配,特点是:线性空间连续,但是对应的物理地址空间不一定连续。vmalloc分配的线性地址所对应的物理页可能处于低端内存,也可能在高端内存。

永久内存映射区

Persistent Kernel Mapping Region该区域可访问高端内存,访问方法是使用alloc_page(_GFP HIGHMEM)分配高端内存页或者使用kmap函数将分配待的高端内存映射到该区域。

固定映射区

Fixing kernel Mapping Region 该区域和 4G 的顶端只有 4k 的隔离带,其每个地址项都服务于特定的用途,如 ACPI_BASE 等。

FP HIGHMEM)分配高端内存页或者使用kmap函数将分配待的高端内存映射到该区域。
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董lucky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值