您的位置 首页 发布

Linux内存寻址和内存办理

Linux内存寻址和内存管理-物理地址空间的顶部以下一段空间,被PCI设备的I/O内存映射占据,它们的大小和布局由PCI规范所决定。640K~1M这段地址空间被BIOS和VGA适配器所占据。

1.    x86的物理地址空间布局

以x86_32,4G RAM为例:

物理地址空间的顶部以下一段空间,被PCI设备的I/O内存映射占有,它们的巨细和布局由PCI标准所决议。640K~1M这段地址空间被BIOS和VGA适配器所占有。

因为这两段地址空间的存在,导致相应的RAM空间不能被CPU所寻址(当CPU拜访该段地址时,北桥会主动将意图物理地址“路由”到相应的I/O设备上,不会发送给RAM),然后构成RAM空泛

当敞开分段分页机制时,典型的x86寻址进程为

内存寻址的作业是由Linux内核和MMU共同完成的,其间Linux内核担任cr3,gdtr等寄存器的设置,页表的保护,页面的办理,MMU则进行具体的映射作业。

2.    Linux的内存办理

Linux采用了分页的内存办理机制。因为x86体系的分页机制是依据分段机制的,因而,为了运用分页机制,分段机制是无法防止的。为了下降复杂性,Linux内核将一切段的基址都设为0,段限长设为4G,只是在段类型和段拜访权限上有所区别,而且Linux内核和一切进程同享1个GDT,不运用LDT(即体系中一切的段描述符都保存在同一个GDT中),这是为了敷衍CPU的分段机制所能做的最少作业。

Linux内存办理机制能够分为3个层次,从下而上依次为物理内存的办理、页表的办理、虚拟内存的办理。

3.    页表办理

为了坚持兼容性,Linux最多支撑4级页表,而在x86上,实践只用了其间的2级页表,即PGD(页大局目录表)和PT(页表),中心的PUD和PMD所占的位长都是0,因而关于x86的MMU是不行见的。

在内核源码中,分别为PGD,PUD,PMD,PT界说了相应的页表项,即

(界说在include/asm-generic/page.h中)

typedef struct {unsigned long pgd;} pgd_t;

typedef struct {unsigned long pud;} pud_t;

typedef struct {unsigned long pmd;} pmd_t;

typedef struct {unsigned long pte;} pte_t;

为了便利的操作页表项,还界说了以下宏:

(界说在arch/x86/include/asm/pgtable.h中)

mk_pte

pgd_page/pud_page/pmd_page/pte_page

pgd_alloc/pud_alloc/pmd_alloc/pte_alloc

pgd_free/pud_free/pmd_free/pte_free

set_pgd/ set_pud/ set_pmd/ set_pte

4.    物理内存办理

Linux内核是以物理页面(也称为page frame)为单位办理物理内存的,为了便利的记载每个物理页面的信息,Linux界说了page结构体:

(坐落include/linux/mm_types.h)

struct page {

unsigned long flags;         

atomic_t _count;       

union {

atomic_t _mapcount;      

struct {          /* SLUB */

u16 inuse;

u16 objects;

};

};

union {

struct {

unsigned long private;            

struct address_space *mapping;   

};

struct kmem_cache *slab;      /* SLUB: Pointer to slab */

struct page *first_page;  /* Compound tail pages */

};

union {

pgoff_t index;             /* Our offset within mapping. */

void *freelist;             /* SLUB: freelist req. slab lock */

};

struct list_head lru;          

};

Linux体系在初始化时,会依据实践的物理内存的巨细,为每个物理页面创立一个page目标,一切的page目标构成一个mem_map数组。

进一步,针对不同的用处,Linux内核将一切的物理页面区分到3类内存办理区中,如图,分别为ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。

ZONE_DMA的规模是0~16M,该区域的物理页面专门供I/O设备的DMA运用。之所以需求独自办理DMA的物理页面,是因为DMA运用物理地址拜访内存,不经过MMU,而且需求接连的缓冲区,所以为了能够供给物理上接连的缓冲区,有必要从物理地址空间专门区分一段区域用于DMA。

ZONE_NORMAL的规模是16M~896M,该区域的物理页面是内核能够直接运用的。

ZONE_HIGHMEM的规模是896M~完毕,该区域即为高端内存,内核不能直接运用。

内存办理区

内核源码中,内存办理区的结构体界说为

struct zone {

struct free_area  free_area[MAX_ORDER];

spinlock_t            lru_lock;      

struct zone_lru {

struct list_head list;

} lru[NR_LRU_LISTS];

struct zone_reclaim_stat reclaim_stat;

unsigned long             pages_scanned;     /* since last reclaim */

unsigned long             flags;               /* zone flags, see below */

atomic_long_t            vm_stat[NR_VM_ZONE_STAT_ITEMS];

unsigned int inactive_raTIo;

wait_queue_head_t   * wait_table;

unsigned long             wait_table_hash_nr_entries;

unsigned long             wait_table_bits;

struct pglist_data       *zone_pgdat;

unsigned long             zone_start_pfn;

};

其间zone_start_pfn表明该内存办理区在mem_map数组中的索引。

内核在分配物理页面时,通常是一次性分配物理上接连的多个页面,为了便于快速的办理,内核将接连的闲暇页面组成闲暇区段,巨细是2、4、8、16…等,然后将闲暇区段按巨细放在不同行列里,这样就构成了MAX_ORDER个行列,也就是zone里的free_area数组。这样在分配物理页面时,能够快速的定位刚好满意需求的闲暇区段。这一机制称为buddy system。

当开释不必的物理页面时,内核并不会当即将其放入闲暇行列(free_area),而是将其刺进非活动行列lru,便于再次时能够快速的得到。每个内存办理区都有1个inaciTIve_clean_list。别的,内核中还有3个大局的LRU行列,分别为acTIve_list,inacTIve_dirty_list和swapper_space。其间active_list用于记载一切被映射了的物理页面,inactive_dirty_list用于记载一切断开了映射且未被同步到磁盘交流文件中的物理页面,swapper_space则用于记载换入/换出到磁盘交流文件中的物理页面。

物理页面分配

分配物理内存的函数首要有

struct page * __alloc_pages(zonelist_t *zonelist, unsigned long order);

参数zonelist即从哪个内存办理区中分配物理页面,参数order即分配的内存巨细。

__get_free_pages(unsigned int flags,unsigned int order);

参数flags可选GFP_KERNEL或__GFP_DMA等,参数order同上。

该函数能够分配物理上接连的内存区域,得到的虚拟地址与物理地址是一一对应的。

void * kmalloc(size_t size,int flags);

该函数能够分配物理上接连的内存区域,得到的虚拟地址与物理地址是一一对应的。

物理页面收回

当闲暇物理页面缺乏时,就需求从inactive_clean_list行列中挑选某些物理页面刺进闲暇行列中,假如依然缺乏,就需求把某些物理页面里的内容写回到磁盘交流文件里,腾出物理页面,为此内核源码中为磁盘交流文件界说了:

(坐落include/linux/swap.h)

struct swap_info_struct {

unsigned long      flags;            /* SWP_USED etc: see above */

signed short prio;              /* swap priority of this type */

signed char  type;             /* strange name for an index */

signed char  next;             /* next type on the swap list */

unsigned char *swap_map;     /* vmalloc'ed array of usage counts */

struct block_device *bdev;      /* swap device or bdev of swap file */

struct file *swap_file;              /* seldom referenced */

};

其间swap_map数组每个元素代表磁盘交流文件中的一个页面,它记载相应磁盘交流页面的信息(如页面基址、所属的磁盘交流文件),跟页表项的效果相似。

收回物理页面的进程由内核中的两个线程专门担任,kswapd和kreclaimd,它们定时的被内核唤醒。kswapd首要经过3个进程收回物理页面:

调用shrink_inactive_list ()扫描inacive_dirty_pages行列,将非活泼行列里的页面写回到交流文件中,并转移到inactive_clean_pages行列里。

调用shrink_slab ()收回slab机制保存的闲暇页面。

调用shrink_active_list ()扫描active_list行列,将活泼行列里可转入非活泼行列的页面转移到inactive_dirty_list。

5.    虚拟内存办理

Linux虚拟地址空间布局如下

Linux将4G的线性地址空间分为2部分,0~3G为user space,3G~4G为kernel space。

因为敞开了分页机制,内核想要拜访物理地址空间的话,有必要先树立映射联系,然后经过虚拟地址来拜访。为了能够拜访一切的物理地址空间,就要将悉数物理地址空间映射到1G的内核线性空间中,这明显不行能。所以,内核将0~896M的物理地址空间1对1映射到自己的线性地址空间中,这样它便能够随时拜访ZONE_DMA和ZONE_NORMAL里的物理页面;此刻内核剩余的128M线性地址空间缺乏以彻底映射一切的ZONE_HIGHMEM,Linux采取了动态映射的办法,即按需的将ZONE_HIGHMEM里的物理页面映射到kernel space的最终128M线性地址空间里,运用完之后开释映射联系,以供其它物理页面映射。尽管这样存在功率的问题,可是内核究竟能够正常的拜访一切的物理地址空间了。

内核空间布局

下面是内核空间布局的具体内容,

在kernel image下面有16M的内核空间用于DMA操作。坐落内核空间高端的128M地址首要由3部分组成,分别为vmalloc area,耐久化内核映射区,暂时内核映射区。

因为ZONE_NORMAL和内核线性空间存在直接映射联系,所以内核会将频频运用的数据如kernel代码、GDT、IDT、PGD、mem_map数组等放在ZONE_NORMAL里。而将用户数据、页表(PT)等不常用数据放在ZONE_ HIGHMEM里,只在要拜访这些数据时才树立映射联系(kmap())。比方,当内核要拜访I/O设备存储空间时,就运用ioremap()将坐落物理地址高端的mmio区内存映射到内核空间的vmalloc area中,在运用完之后便断开映射联系。

用户空间布局

在用户空间中,虚拟内存和物理内存或许的映射联系如下图

当RAM满足多时,内核会将用户数据保存在ZONE_ HIGHMEM,然后为内核腾出内存空间。

下面是用户空间布局的具体内容,

用户进程的代码区一般从虚拟地址空间的0x08048000开端,这是为了便于查看空指针。代码区之上就是数据区,未初始化数据区,堆区,栈区,以及参数、大局环境变量。

虚拟内存区段

为了办理不同的虚拟内存区段,Linux代码中界说了

(坐落include/linux/mm_types.h)

struct vm_area_struct {

struct mm_struct * vm_mm;   /* The address space we belong to. */

unsigned long vm_start;          /* Our start address within vm_mm. */

unsigned long vm_end;            /* The first byte after our end address

within vm_mm. */

/* linked list of VM areas per task, sorted by address */

struct vm_area_struct *vm_next, *vm_prev;

pgprot_t vm_page_prot;         /* Access permissions of this VMA. */

unsigned long vm_flags;          /* Flags, see mm.h. */

};

其间vm_start,vm_end界说了虚拟内存区段的开始方位,vm_page_prot和vm_flags界说了拜访权限等。

vm_next构成一个链表,保存同一个进程的一切虚拟内存区段。

vm_mm指向进程的mm_struct结构体,它的界说为

(坐落include/linux/mm_types.h)

struct mm_struct {

struct vm_area_struct * mmap;            /* list of VMAs */

struct rb_root mm_rb;

struct vm_area_struct * mmap_cache;       /* last find_vma result */

unsigned long mmap_base;            /* base of mmap area */

unsigned long task_size;          /* size of task vm space */

unsigned long cached_hole_size;

unsigned long free_area_cache;          

pgd_t * pgd;

atomic_t mm_users;                /* How many users with user space? */

atomic_t mm_count;              

};

每个进程只要1个mm_struct结构,保存在task_struct结构体中。

与虚拟内存办理相关的结构体联系图如下

虚拟内存相关函数

创立一个内存区段能够用

unsigned long get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags);

当给定一个虚拟地址时,能够查找它所属的虚拟内存区段:

struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr);

因为一切的vm_area_struct组成了一个RB树,所以查找的速度很快。

向用户空间中刺进一个内存区段能够用

void insert_vm_struct (struct mm_struct *mm, struct vm_area_struct *vmp);

运用以下函数能够在内核空间分配一段接连的内存(但在物理地址空间上不一定接连):

void *vmalloc(unsigned long size);

运用以下函数能够将ZONE_HIGHMEM里的物理页面映射到内核空间:

static inline void *kmap(struct page*page);

6.    内存办理3个层次的联系

下面以扩展用户仓库为例,解说3个层次的联系。

调用函数时,会触及仓库的操作,当拜访地址超越仓库的鸿沟时,便引起page fault,内核处理页面失效的进程中,触及到内存办理的3个层次。

Ø 调用expand_stack()修正vm_area_struct结构,即扩展仓库区的虚拟地址空间;

Ø 创立空白页表项,这一进程会使用mm_struct中的pgd(页大局目录表基址)得到页目录表项(pgd_offset()),然后核算得到相应的页表项(pte_alloc())地址;

Ø 调用alloc_page()分配物理页面,它会从指定内存办理区的buddy system中查找一块适宜的free_area,从而得到一个物理页面;

Ø 创立映射联系,先调用mk_pte()发生页表项内容,然后调用set_pte()写入页表项。

Ø 至此,扩展仓库基本完成,用户进程从头拜访仓库便能够成功。

能够以为,结构体pgd和vm_area_struct,函数alloc_page()和mk_pte()是衔接三者的桥梁。

 

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/changshang/fabu/90615.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部