首页 第九讲 虚拟内存管理(一)

第九讲 虚拟内存管理(一)

举报
开通vip

第九讲 虚拟内存管理(一)nullLinux内核源代码导读Linux内核源代码导读哈尔滨工业大学(威海) 嵌入式系统实验室 Spring 2012Linux虚拟内存管理Linux虚拟内存管理 1 描述物理内存 2 页表管理 3 进程地址空间1 描述物理内存1 描述物理内存均质存储结构与非均质存储结构 传统的计算机中,整个物理空间都是均匀一致的,CPU访问这个空间中任何一个地址所需时间都是相同的,所以称为“均质存储结构(Uniform Memory Architecture)”,简称UMA *null假设一台的计算机中具有这样的结构: ...

第九讲 虚拟内存管理(一)
nullLinux内核源代码导读Linux内核源代码导读哈尔滨工业大学(威海) 嵌入式系统实验室 Spring 2012Linux虚拟内存管理Linux虚拟内存管理 1 描述物理内存 2 页 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf 管理 3 进程地址空间1 描述物理内存1 描述物理内存均质存储结构与非均质存储结构 传统的计算机中,整个物理空间都是均匀一致的,CPU访问这个空间中任何一个地址所需时间都是相同的,所以称为“均质存储结构(Uniform Memory Architecture)”,简称UMA *null假设一台的计算机中具有这样的结构: 在这样的系统中,其物理存储空间虽然连续,“质地”却不一致,CPU访问各内存区域速度不一样,所以称为“非均质存储结构(Non-Uniform Memory Architecture)”,简称NUMA*总 线CPU内存CPU内存CPU内存CPU内存null节点(Node) 在NUMA 结构的系统中,属于同一个CPU节点的存储器模块通常是匀质的,因此也被称为“存储节点” (Memory Node) 在Linux中,节点用一个名为pg_data_t的结构体来表示。所有的节点通过pg_data_t->node_next指针被链入一个叫做pgdat_list的单向链表 UMA结构的计算机只有一个节点 分配一个页面时,Linux采取node-local allocation policy从距离当前CPU最近的节点分配 *null管理区(Zone) 每个节点内的物理内存又按照其地址被划分为几个管理区(zone)。使用一个叫做zone_t的结构体来代表每个管理区 管理区的类型可以是ZONE_DMA,ZONE_NORMAL或ZONE_HIGHMEM ZONE_DMA:用于DMA ZONE_NORMAL:可以直接映射到内核的区域 ZONE_HIGHMEM:高端内存,不能被直接映射到内核*nullWith the x86 the zones are: ZONE_DMA First 16MiB of memory ZONE_NORMAL 16MiB – 896MiB ZONE_HIGHMEM 896MiB – End*页面(page)页面(page)系统的内存被划分为固定大小的块,称为页框(page frame)。每个物理页框由一个page(或mem_map_t)结构体表示 内核在初始化时根据物理内存的大小建立起一个page结构体数组mem_map,作为物理页框的仓库 每个page结构在这个数组内的下标就是该物理页框的序号 mem_map数组通常放在ZONE_NORMAL的开始处*Nodes,Zones 和 Pages关系图Nodes,Zones 和 Pages关系图*pagepagepagepagepagepagezone_mem_mapzone_mem_mapzone_mem_mapZONE_DMAZONE_NORMALZONE_HIGHMEMnode_zonespage_data_t节点(Nodes)节点(Nodes)如前所述,一个节点用pg_data_t结构代表。在声明: * typedef struct pglist_data { zone_t node_zones[MAX_NR_ZONES];//节点包括的的管理区 zonelist_t node_zonelists[GFP_ZONEMASK+1]; int nr_zones; //管理区数量 struct page *node_mem_map; //该节点的第一个页面 unsigned long *valid_addr_bitmap; //描述节点内空洞的位图 struct bootmem_data *bdata; unsigned long node_start_paddr; //节点起始物理地址 unsigned long node_start_mapnr;//在mem_map数组内的偏移 unsigned long node_size; //节点大小,即总页面数 int node_id; //节点编号 struct pglist_data *node_next; //指向下一个节点的指针 } pg_data_t;管理区(Zones)管理区(Zones)一个管理区用zone_t结构代表。在声明: *typedef struct zone_struct { spinlock_t lock; //保护该结构的自旋锁 unsigned long free_pages; //空闲页面总数 unsigned long pages_min, pages_low, pages_high; int need_balance; //是否需要kswaped来平衡内存分配 free_area_t free_area[MAX_ORDER]; //空闲区位图 wait_queue_head_t * wait_table; //等待释放页面的进程哈希表 unsigned long wait_table_size; //上述哈希表大小 unsigned long wait_table_shift; null续* struct pglist_data *zone_pgdat; //指向所属节点 struct page *zone_mem_map; //管理区第一个页面 unsigned long zone_start_paddr; //管理区起始物理地址 unsigned long zone_start_mapnr; //在mem_map数组内的偏移 char *name; //管理区名称 unsigned long size; //管理区大小,以页面数计 } zone_t;页面(Pages)页面(Pages)每一个物理页框都关联一个page结构体,用来追踪页框的状态。page结构体声明于:*typedef struct page { struct list_head list; struct address_space *mapping; //映射到文件时使用 unsigned long index; struct page *next_hash; //映射到文件时的hash链 atomic_t count; //引用计数 unsigned long flags; //状态标记 struct list_head lru; //LRU队列头 struct page **pprev_hash; struct buffer_head * buffers; #if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL) void *virtual; //页面映射到内核空间时的地址 #endif } mem_map_t;2 页表管理2 页表管理要解决的主要问题: Linux的地址映射模型? 怎样组织页表来描述映射模型? 怎样用线性地址来找到所属物理页面? 虚拟地址物理地址*2.1 描述页目录2.1 描述页目录每个进程都拥有一个pgd_t结构体数组,占据一个物理页框,叫作全局页目录(Page Global Directory,简称PGD) 在x86上,通过将PGD的地址写入CR3寄存器来加载一个进程的页表 每个pgd_t指向一个中间页目录(Page Middle Directory,简称PMD),这是一个pmd_t结构体数组。每个PMD也占一个物理页框 每个pmd_t又指向一个Page Table Entries(PTE),这是一个pte_t结构体数组,大小同样为一个页框 每个pte_t最终指向包含用户数据的物理页框*null页目录和页表的表项格式: _PAGE_PRESENT—页面在内存没有被交换出去 _PAGE_PROTNONE-- Page is resident but not accessable _PAGE_RW-- Set if the page may be written to _PAGE_USER-- Set if the page is accessible from user space _PAGE_DIRTY-- Set if the page is written to _PAGE_ACCESSED-- Set if the page is accessed 线性地址的组成: *nullPage Table Layout*nullx86的两层映射示意图*123456null例:一段follow_page()代码*pgd_t *pgd; pmd_t *pmd; pte_t *ptep, pte; pgd = pgd_offset(mm, address); //取得全局页目录表项 if (pgd_none(*pgd) || pgd_bad(*pgd)) goto out; pmd = pmd_offset(pgd, address); //取得中间页目录表项指针 if (pmd_none(*pmd) || pmd_bad(*pmd)) goto out; ptep = pte_offset(pmd, address); //取得页表项指针 if (!ptep) goto out; pte = *ptep; //得到页表项2.2 将地址映射到page2.2 将地址映射到pageLinux需要一种快速的方法来将虚拟地址到物理地址,并将page结构映射到它的物理地址 通过一个全局的mem_map数组来实现 mem_map是个page结构体数组,每个pages对应一个物理页框 每个page结构在这个数组内的下标就是它对应的物理页框的序号 *null将page结构映射到物理地址 在x86结构上是简单的线性映射,只需将物理地址加上3GB就得到其内核虚拟地址 */* from */ #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) /* from */ static inline unsigned long virt_to_phys(volatile void * address) { return __pa(address); }null将page结构映射到物理地址 将物理地址右移PAGE_SHIFT位后当作mem_map数组的下标即可找到对应的page结构 virt_to_page将虚拟地址kaddr作为输入参数,通过__pa()宏将其转为物理地址,再将其右移PAGE_SHIFT位转为数组下标,加上mem_map首地址最终得到对应的page结构*#define virt_to_page(kaddr) (mem_map + (__pa(kaddr)>>PAGE_SHIFT))3 进程地址空间3 进程地址空间现代OS采用虚拟存储器,每个进程都有自己的虚拟地址空间,由OS负责映射到物理内存 地址空间 内核角度看到的地址空间和进程角度看到的不一样 用户空间随着进程切换而改变,内核空间由所有进程共有 *3.1 线性地址空间(Linear Address Space)3.1 线性地址空间(Linear Address Space)从进程的角度看,地址空间是平直的,线性的 从内核角度看,线性地址被分为两部分:用户空间和内核空间 每个进程有自己的用户空间,因此用户空间随着进程的切换而切换(请思考:具体怎样切换?) 内核空间为所有进程共有,不随进程切换改变。实际上这部分是内核代码运行的内存空间 x86系统结构下,用户空间占据0~3GB,内核空间占据3~4GB*null图:内核地址空间*3.2 管理地址空间3.2 管理地址空间在Linux中,由一个mm_struct结构体代表进程的地址空间 进程的task_struct结构中有个叫做mm指针,指向一个mm_struct结构体 每个地址空间由一些页框对齐的内存区域组成,这些区域用vm_area_struct结构体代表 每个vm_area_struct中包含相同保护属性,用于相同目的页面 例如,一个区域用于动态分配内存的堆区,另一个用于装载共享库 *null当备份一个内存区域到文件时,系统会设置该内存区域vm_area_struct结构的vm_file域 此时vm_file->f_dentry->i_mapping指向该区域的address_space结构。address_space含有所有特定文件系统相关信息,用来操作磁盘上的页面*null*表:影响进程地址空间和内存区域的系统调用3.3 进程地址空间描述符3.3 进程地址空间描述符进程的地址空间由一个叫做mm_struct的结构代表 每个进程只有一个mm_struct,同一进程的线程之间则共享mm_struct 一般来说,内核线程不需要mm_struct。其task_struct的mm域总为NULL *null*struct mm_struct { struct vm_area_struct * mmap; rb_root_t mm_rb; struct vm_area_struct * mmap_cache; pgd_t * pgd; atomic_t mm_users; atomic_t mm_count; int map_count; struct rw_semaphore mmap_sem; spinlock_t page_table_lock; struct list_head mmlist; unsigned long start_code, end_code, start_data, end_data; unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end; unsigned long rss, total_vm, locked_vm; unsigned long def_flags; unsigned long cpu_vm_mask; unsigned long swap_address; unsigned dumpable:1; mm_context_t context; };null与mm_struct相关的函数*3.4 虚存区域(vm_area_struct)3.4 虚存区域(vm_area_struct)进程的地址空间由若干互不重叠虚存区域组成,不同的虚存区域可能具有不同的访问权限和属性 提供更好的的安全性,灵活性 进程的所有内存区域可在/proc/PID/maps下查看 虚存区域映射到文件时,可以从vm_area_struct的vm_file指针找到对应文件的file结构体 file结构包含一个指向其inode的指针,inode结构则含有指向address_space结构的指针。address_space结构包含含一组文件系统函数指针,这些函数用来读写磁盘上的页面*null*struct vm_area_struct { struct mm_struct * vm_mm; // 所属的mm_struct结构体 unsigned long vm_start; // 其实地址 unsigned long vm_end; // 结束地址 /* linked list of VM areas per task, sorted by address */ struct vm_area_struct *vm_next; // 链表中下一个vm_area_struct pgprot_t vm_page_prot; // 页面的保护属性 unsigned long vm_flags; // 内存区域的标志和保护属性 rb_node_t vm_rb; // 红黑树 struct vm_area_struct *vm_next_share; struct vm_area_struct **vm_pprev_share; /* Function pointers to deal with this struct. */ struct vm_operations_struct * vm_ops; // 一组操作vm_area的函数 /* Information about our backing store: */ unsigned long vm_pgoff; struct file * vm_file; // 备份到文件时对应的file结构体 unsigned long vm_raend; void * vm_private_data; // 私有数据 };vm_flags标志vm_flags标志*null与mmap相关的标志*null*图:与进程地址空间有关的数据结构关系图null当一个虚存区域备份到文件时,关联的address_space结构定义如下:*struct address_space { struct list_head clean_pages; //干净页面 struct list_head dirty_pages; //脏页面 struct list_head locked_pages; //锁住的页面 unsigned long nrpages; //总页面数 struct address_space_operations *a_ops; //操作函数 struct inode *host; // 备份文件的inode struct vm_area_struct *i_mmap; //vma struct vm_area_struct *i_mmap_shared; spinlock_t i_shared_lock; //保护结构体的自旋锁 int gfp_mask; //__alloc_pages()分配新页面的标志 };null*struct address_space_operations { int (*writepage)(struct page *); int (*readpage)(struct file *, struct page *); int (*sync_page)(struct page *); /* * ext3 requires that a successful prepare_write() * call be followed * by a commit_write() call - they must be balanced */ int (*prepare_write)(struct file *, struct page *, unsigned, unsigned); int (*commit_write)(struct file *, struct page *, unsigned, unsigned); /* Unfortunately this kludge is needed for FIBMAP. * Don’t use it */ int (*bmap)(struct address_space *, long); int (*flushpage) (struct page *, unsigned long); int (*releasepage) (struct page *, int); #define KERNEL_HAS_O_DIRECT int (*direct_IO)(int, struct inode *, struct kiobuf *, unsigned long, int); };创建一个虚存区域创建一个虚存区域系统调用mmap()用来在一个进程的地址空间内创建一个新的虚存区域。在x86下该系统调用的服务程序为do_mmap2()。 如果是映射一个文件,do_mmap2()会先检查调用参数对应的struct file,然后取得mm_struct->mmap_sem信号量 下一步,do_mmap2()调用do_map_pgoff()函数,这是创建一个虚存区域的主要函数*nulldo_map_pgoff()函数主要过程: sanity checks。包括映射文件或外设时对应文件或外设的相关操作函数是否存在,映射区域大小是否页面对齐,是否过大,不能映射到内核空间等等 在线性地址空间找到一块足够大的空闲区域,由get_unmaped_area()函数完成 检查VM标志和文件访问权限 分配一个vm_area_struct并初始化它 将新VMA链入相应链表 调用特定的文件系统或设备mmap函数 更新统计数据并返回 *查找一个已映射的虚存区域查找一个已映射的虚存区域在内核中,查找一个给定线性地址所属的VMA是一个经常用到的操作,由find_vma()等函数实现 首先检查mmap_cache域,这是上次查找调用该函数的结果(访存的局部性原理) cache中没有则去mm_rb域指向的红黑树中搜索 如果指定的地址没有所属的VMA,则函数会找到一个离该地址最近的VMA返回。因此,调用者必须检查返回的VMA是否包含指定地址*重映射或移动一个虚存区域重映射或移动一个虚存区域Linux提供系统调用mremap()来扩张或缩小一个已映射的虚存区域。*函数调用图:sys_mremap()null若要移动一个虚存区域,do_mremap()首先调用get_unmapped_area()找到一个能包含新设置的容量的虚存区域,然后调用move_area()将旧的VMA移动到新区*函数调用图:move_vma()3.5 页面错误(Page Faulting)3.5 页面错误(Page Faulting)什么是Page Fault? 访问一个不在内存中的页面,例如,页面已被交换到磁盘 访问权限问题,例如试图写一个只读页面 发生页面错误时,CPU通过中断机制自动陷入到内核态对错误进行处理 内核为每种硬件平台提供特定的页面异常处理程序 ,do_page_fault() *
本文档为【第九讲 虚拟内存管理(一)】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_639465
暂无简介~
格式:ppt
大小:832KB
软件:PowerPoint
页数:0
分类:互联网
上传时间:2012-04-08
浏览量:51