新闻资讯
Group news
青岛广盛源肥业有限公司    您的位置: 首页  >  新闻资讯  >  正文

Linux内存寻址和内存管理

2019年10月11日 文章来源:网络整理 热度:112℃ 作者:刘英

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

Linux内存寻址和内存管理

以x86_32,4G RAM为例:

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

由于这两段地址空间的存在,导致相应的RAM空间不能被CPU所寻址(当CPU访问该段地址时,北桥会自动将目的物理地址“路由”到相应的I/O设备上,不会发送给RAM),从而形成RAM空洞。

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

Linux内存寻址和内存管理

内存寻址的工作是由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是不可见的。

Linux内存寻址和内存管理

在内核源码中,分别为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。

Linux内存寻址和内存管理

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数组中的索引。

上一篇:Linux用日志打印的方式调试程序


下一篇:详细解读Linux的输入子系统

友情链接
Links