1 / 52

第二章 LINUX 存储管理

第二章 LINUX 存储管理. LINUX 的分页管理机制. LINUX 的地址划分. 每一个用户进程都可以访问 4GB 的线性虚拟内存空间。 从 0 到 3GB 的虚拟内存地址是用户空间,用户进程可以直接对其进行访问。 从 3GB 到 4GB 的虚拟内存地址为核心态空间,存放仅供核心态访问的代码和数据,用户态进程不可访问。 所有进程从 3GB 到 4GB 的虚拟空间都是一样的,有同样的页目录项,同样的页表,对应到同样的物理内存段。 LINUX 以此方式让内核态进程共享代码段和数据段。

Download Presentation

第二章 LINUX 存储管理

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第二章 LINUX存储管理 LINUX的分页管理机制

  2. LINUX的地址划分 • 每一个用户进程都可以访问4GB的线性虚拟内存空间。 • 从0到3GB的虚拟内存地址是用户空间,用户进程可以直接对其进行访问。 • 从3GB到4GB的虚拟内存地址为核心态空间,存放仅供核心态访问的代码和数据,用户态进程不可访问。 • 所有进程从3GB到4GB的虚拟空间都是一样的,有同样的页目录项,同样的页表,对应到同样的物理内存段。LINUX以此方式让内核态进程共享代码段和数据段。 • 内核态虚拟空间从3GB到3GB+4M的一段(也就是进程页目录第768项所管辖的范围),被映射到物理空间0到4M段。因此,进程处于核心态时,只要通过访问虚拟空间3GB到3GB+4M段,偏移地址0到4M,即访问了物理空间0到4M段。

  3. 虚拟地址转换

  4. mm_struct(include/linux/sched.h) 203 struct mm_struct { 204 struct vm_area_struct * mmap; /* list of VMAs */ 205 struct vm_area_struct * mmap_avl; /* tree of VMAs */ 206 struct vm_area_struct * mmap_cache; /* last find_vma result */ 207pgd_t * pgd; 208atomic_t mm_users; /* How many users with user space? */ 209atomic_t mm_count; /* How many 进程/线程 refer to "struct mm_struct" (users count as 1) */ 210 int map_count; /* number of VMAs */ 211 struct semaphore mmap_sem; /* 对mmap操作的信号量 */ 212spinlock_t page_table_lock; 214 struct list_head mmlist; /* List of all active mm's */ 216 unsigned long start_code, end_code, start_data, end_data; 217 unsigned long start_brk, brk, start_stack; 218 unsigned long arg_start, arg_end, env_start, env_end; 219 unsigned long rss, total_vm, locked_vm; 220 unsigned long def_flags; 221 unsigned long cpu_vm_mask; 222 unsigned long swap_cnt; /* number of pages to swap on next pass */ 223 unsigned long swap_address; 226mm_context_t context; /* Architecture-specific MM context */ 227 };

  5. PCB对内存的控制(之一) struct mm_struct结构的成员 pgd_t * pgd; 表示进程页目录的起始地址。

  6. PCB对内存的控制

  7. 第二章 LINUX存储管理 虚存段(vma)的组织和管理 (克服页表中空表项过烂问题)

  8. vma段(vitual memory area) 一个vma段是某个进程的一段连续的虚存空间; 在这段虚存里的所有单元拥有相同的特征。例如:属于同一进程,相同的访问权限,同时被锁定(locked),同时受保护(protected),等等。

  9. vm_area_struct(include/linux/mm.h) 41 struct vm_area_struct { 42 struct mm_struct * vm_mm; /* VM area parameters */ 43 unsigned long vm_start; 44 unsigned long vm_end; 47 struct vm_area_struct *vm_next; /* linked list of VM areas per task, sorted by address */ 49 pgprot_t vm_page_prot; 50 unsigned long vm_flags; 53 short vm_avl_height; /* AVL tree of VM areas per task, sorted by address */ 54 struct vm_area_struct * vm_avl_left; 55 struct vm_area_struct * vm_avl_right; 61 struct vm_area_struct *vm_next_share; 62 struct vm_area_struct **vm_pprev_share; 64 struct vm_operations_struct * vm_ops; 65 unsigned long vm_pgoff; /* offset in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */ /* 如果vma段的内容是关于文件的,则vm_offset就是该段内容相对于文件起始位置的偏移量。 */ 66 struct file * vm_file; 67 unsigned long vm_raend; 68 void * vm_private_data; /* was vm_pte, 用于共享内存,含SHM_SWP_TYPE和共享内存段id号 */ 69 };

  10. vma段的链表 进程通常占用几个vma段,分别用于代码段、数据段、堆栈段等。属于同一进程的vma段通过vm_next指针连接,组成链表。如图。 struct mm_struct结构的成员struct vm_area_struct * mmap 表示进程的vma链表的表头。

  11. PCB对内存的控制(之二)

  12. PCB对内存的控制(之三) LINUX同时维护了一个AVL(Adelson-Velskii and Landis)树。 在树中,所有vma段均有左指针vm_avl_left指向相邻的低地址虚存段,右指针vm_avl_right指向相邻的高地址虚存段。 struct mm_struct结构的成员struct vm_area_struct * mmap_avl表示进程的AVL树的根,vm_avl_height表示AVL树的高度。 对vma段可以进行加锁、加保护等操作。

  13. AVL树

  14. 第二章 LINUX存储管理 物理空间管理

  15. 物理空间的组织(include/linux/fs.h,struct page) 物理内存以页帧(page frame)为单位,页帧的长度固定,等于页长,对INTEL CPU缺省为4K字节。 LINUX对物理内存的管理通过mem_map表描述(mm/memory.c)。 mem_map在系统初始化时由free_area_init()函数创建(mm/page_alloc.c)。 它本身是关于struct page mem_map_t (linux/mm.h)的数组,每项mem_map_t对应一个关于核心态、用户态代码和数据等的页帧。

  16. mem_map在物理空间的位置

  17. struct page 134 typedef struct page { 135 struct list_head list; 136 struct address_space *mapping; /* 见书中解释 */ 137 unsigned long index; /* 若该页帧的内容是文件,则index指出文件的inode和偏移位置 */ 138 struct page *next_hash; 139 atomic_t count; /* 指明目前使用该页面的用户数。count==0意味着此页空闲 */ 140 unsigned long flags; /* atomic flags, some possibly updated asynchronously */ 141 struct list_head lru; 142 unsigned long age; /* 页帧的年龄,越小越先换出 */ 143 wait_queue_head_t wait; 144 struct page **pprev_hash; 145 struct buffer_head * buffers; /* 若该页帧作为缓冲区,则指示地址 */ 146 void *virtual; /* non-NULL if kmapped */ 147 struct zone_struct *zone; 148 } mem_map_t;

  18. 第二章 LINUX存储管理 空闲物理内存管理 (Buddy System Impelmentation Codes)

  19. 空闲内存的组织

  20. bitmap 表 在物理内存低端,紧跟mem_map表的bitmap表以位图方式记录了所有物理内存的空闲状况。 与mem_map一样,bitmap表在系统初始化时由free_area_init()函数创建(mm/page_alloc.c)。 与一般性位图不同的是,bitmap表分割成NR_MEM_LISTS组(缺省值6)。

  21. bitmap 表 首先是第0组,初始化时设定了长度为(end_mem-start_mem) / PAGE_SIZE/20+3,每位表示20个页帧的空闲状况,置位表示已被占用。 接着是第1组,初始化时设定了长度为: (end_mem-start_mem) / PAGE_SIZE/21+3 ,每位表示连续21个页帧的空闲状况,置位表示其中1页或2页已被占用。 类似地,对第i组,初始化时设定了长度为: (end_mem-start_mem) / PAGE_SIZE / 2i+3 ,每位表示连续2i个页帧的空闲状况,置位表示其中1页或几页已被占用。 例如对第5组,某个bit所对应的连续32页帧中只要有一个被占用,此位即置1,只有当所有32页帧全部回收后才清0。

  22. free_area数组 LINUX用free_area数组记录空闲的物理页帧。free_area数组由NR_MEM_LISTS个free_area_struct结构类型的数组元素构成,每个元素均作为一条空闲块链表的表头。 struct free_area_struct { struct page *next; /* 此结构的next,prev指针与struct page匹配 */ struct page *prev; unsigned int * map; /* 指向bitmap */ }; static struct free_area_struct free_area[NR_MEM_LISTS]; 所有单个空闲页帧组成的链表挂到free_area数组的第0项后面。连续2 i个空闲页帧则被挂到free_area数组的第i项后面。

  23. 操作函数 • 分配内存块由__get_free_pages()函数和宏定义__get_free_page()执行 • 释放内存块可以调用free_pages()函数执行。

  24. 分配算法 LINUX采用buddy算法分配空闲块,块长可以是2i个 (0<= i< NR_MEM_LISTS) 页帧。 当分配长度是2i页帧的块时,从free_area数组的第i条链表开始搜索,找不到再搜索第i+1条链表,余此类推。 若找到的空闲块长正好等于需求的块长,则直接将它从free_area删除,返回首地址。 若找到的空闲块长大于需求的块长,则将空闲块一分为二,前半部分插入free_area中前一条链表,取后半部分。 若还大,则继续对半分,留一半取一半,直至相等。 bitmap表也相应调整。每分配一个2i页帧长的块,都要将bitmap表从第i组到第NR_MEM_LISTS组的对应的bit置1。

  25. 释放算法 回收空闲块时,change_bit()函数根据bitmap表的对应组,判断回收块的前后邻居是否也为空。 若空则合并,即修改bitmap表中对应位,从free_area的空闲链表中取下该相邻块。 此判断是个递归过程,直至找不到空闲邻居为止。 将最后合并的最大块插入free_area的相应链表中。

  26. 第二章 LINUX存储管理 SLAB

  27. 第二章 LINUX存储管理 核心态内存的申请与释放

  28. 核心态内存 • 核心态内存是用来存放LINUX核心系统数据结构的内存区域,处于进程虚拟空间的3G至4G(精确地说应该是3G+high_memory,其中high_memory是系统在启动阶段测得的物理内存的实际容量)范围内。 • 核心态内存的分配和释放以块(block)为单位。

  29. 核心态空闲内存的组织

  30. blocksize表 blocksize表规定申请内存块时可供选择的块长度: static const unsigned int blocksize[] = { 32, 64, 128, 252, 508, 1020, 2040, 4096 – 16, 8192 – 16, 16384 – 16, 32768 – 16, 65536 – 16, 131072 – 16, 0 /* 表示blocksize表的结束 */ }; 它们近似等于2的次幂。

  31. sizes表 sizes表管理那些从物理空间free_area申请得到的用于核心态内存空间分配的空闲块。 sizes表的元素类型是struct size_descriptor, 每个表项实质上就是两个由等长空闲块所组成链表的表头,一是DMA可访问的,一是一般性的。

  32. 核心内存的申请和释放 • 核心内存申请 char *kmalloc (unsigned long size, int priority); • 核心内存释放 kfree(char * addr); • 参见文件“kmalloc.doc”

  33. kmalloc cache

  34. 第二章 LINUX存储管理 用户态内存的申请与释放

  35. 用户态内存 由vmalloc()分配的存储空间在进程的虚拟空间是连续的,但它对应的物理内存仍需经缺页中断后,由缺页中断服务程序分配。所分配的物理页帧也不是连续的。这些特征与访问用户态内存相似,所以不妨把vmalloc()和vfree()称作用户态内存的申请与释放界面。 注意,malloc()、free()与vmalloc()、vfree()不是一回事,gcc并不是利用vmalloc、vfree实现malloc、free的。

  36. 用户态内存的位置 可分配的虚拟空间在3G+high_memory+HOLE_8M以上高端,由vmlist链表管理。 3G是核心态赖以访问物理内存的始址,high_memory是安装在计算机中实际可用的物理内存的最高地址,因此3G+high_memory也就是(从虚拟空间中看到的)物理内存的上界。HOLE_8M则是长度为8M的“隔离带”,起到越界保护作用。 尽管vmalloc返回高于任何物理地址的高端地址,但因为vmalloc同时更改了页表甚至页目录,处理器仍能正确地访问这些高端连续地址(页目录和页表把高端虚拟地址映射到实际的物理地址)。内核态程序与进程用户态程序共享同一个页目录,同一组页表,因而内核态程序也能正常访问。

  37. 管理用户态内存的结构

  38. 操作函数 • 申请 void * vmalloc(unsigned long size); • 释放 void vfree(void * addr); • 参见文件vmalloc.doc

  39. kmalloc与vmalloc的比较 • vmalloc返回的地址空间不能绕过CPU的页表机构直接使用。当操作系统某些管理模块(如DMA部件)需要直接使用实际物理地址时,vmalloc不合适,必须用kmalloc。 • 向vmalloc申请小块内存(如1页左右)也不合适,因为无论是内存的分配还是使用,都涉及到内存和页表两方面因素。。 • 比较合适的情形可能是申请大块缓冲区。此时,尽管大缓冲区占用了大段虚拟地址,但实际占用的物理地址不大,因为当前不用的内容不会调入物理内存中。相反,如果用kmalloc申请大缓冲区,那么,将实实在在占用这么大的一块物理内存。何况,缺省情况下,kmalloc可分配的最大内存块只有131054字节。

  40. 第二章 LINUX存储管理 交换空间

  41. 两种交换空间 • 一种用整个块设备,如硬盘的一个分区,称作交换设备,效率较高; • 另一种用文件系统中固定长度的文件,称作交换文件,效率较低。 • LINUX允许并行管理MAX_SWAPFILES个交换空间(MAX_SWAPFILES的缺省值为8)。

  42. 交换空间的格式 • 前4096字节是一个以字符串 “SWAP_SPACE”结尾的位图。位图的每一位(bit)对应一个交换空间的页面,置位表示对应的页面可用于换页操作。 • 第4096字节之后则是真正存放换出页面的空间。 • 这样,每个交换空间最多可容纳 (4096-10)* 8 – 1 = 32687个页面。

  43. 启用交换空间 int sys_swapon(const char * swapfile, int swapflags); • 第一个参数swapfile是设备名或文件名, • swapflags规定交换空间的优先数。该参数中,SWAP_FLAG_PREFER(0X8000)必须置位,SWAP_FLAG_PRIO_MASK(0X7FFF)指定一个正的优先数。如果没有指定优先数,swapon自动给出一个负的优先数,负优先数的取值决定于swapon的调用次数。

  44. 每注册一个交换空间,就在swap_info表中填一项swap_info_struct结构每注册一个交换空间,就在swap_info表中填一项swap_info_struct结构 25 struct swap_info_structswap_info[MAX_SWAPFILES]; 49 struct swap_info_struct { 50 unsigned int flags; /* 如果SWP_USED位置位,则被占用。如果SWP_WRITEOK,则该交换空间准备就绪。 */ 51kdev_t swap_device; /* 对于交换设备,swap_device属性表示交换设备的主、次设备号 */ 52spinlock_t sdev_lock; /* 对于此设备的互斥锁 */ 53 struct dentry * swap_file; /* 对于交换文件,swap_file属性指向该文件的inode */ 54 struct vfsmount *swap_vfsmnt; 55 unsigned short * swap_map;/* 指向一张表,其每一字节按顺序对应交换空间的一个页面,字节的值代表了引用该页面的进程数 */ 56 unsigned int lowest_bit; /*交换空间中的第一个没有被任何进程使用的交换页在swap_map数组中的下标 */ 57 unsigned int highest_bit; /* 交换空间中最后一个没被任何进程使用的交换页的下标 */ 58 unsigned int cluster_next; /*上次从当前的cluster中成功分配的交换页面的后继页面在swap_map数组中的下标 */ 59 unsigned int cluster_nr; /* 当前cluster中可供使用的交换页面的个数 */ 60 int prio; /*交换空间的优先级。优先级越高,交换文件申请交换页面的时候越优先考虑 */ 61 int pages; /* 表示该交换空间尚有多少空闲空间可供保存进程换出的物理页 */ 62 unsigned long max; 63 int next; /*指向下一项交换空间的的指针 */ 64 }; 153 struct swap_list_t { 154 int head; /* head of priority-ordered swapfile list */ 155 int next; /* swapfile to be used next */ 156 }; 23 struct swap_list_t swap_list = {-1, -1};

  45. 注销交换空间 int sys_swapoff(const char * swapfile);

  46. 交换空间的工作 kswapd进程换出页面时,调用try_to_swap_out() 测试页面的年龄。如果某物理页面可以换出,则调用get_swap_page向swap_list.next指示的交换空间申请空闲页面,得到一地址entry。该地址写入进程页表中那个原来描述换出物理页面的页表项,替换了其中的页帧地址。最后,调用rw_swap_page(),将换出的物理页面写到entry指定的交换空间某个页面中。 反过来,当缺页中断发生时,缺页中断服务程序可以根据产生缺页的地址(由CR2寄存器给出),找到描述该页面的页表项。页表项的Present位应该为0,最高20位指出该页面保存在哪个交换空间的哪个页面中。然后,经一系列函数调用后,读入该页面。

  47. 第二章 LINUX存储管理 页交换进程和页面换出

  48. kswapd • 当物理页面不够时,利用kswapd释放部分物理页面,将它们的内容写到交换空间。 • kswapd是一特殊的进程,称内核态线程(kernel thread)。 • 注意,kernel thread完全不同于通常意义上的线程。它是没有虚拟存储空间的进程,它只运行在内核态,直接使用物理地址空间。同类型的进程还有bdflush和init。 • kswapd的作用超越了字面上的描述。它不仅能将页面换出到交换空间(交换区或交换文件),它也保证系统中有足够的空闲页面以保持存储系统高效地运行。

  49. 第二章 LINUX存储管理 缺页中断和页面换入 • 由于只有该映像区的开始部分调入内存,因此,进程迟早会执行到那些尚未调入内存的代码。

  50. 产生缺页中断 • 当一个进程访问了一个还没有有效页表项的虚拟地址时(即页表项的P位为0),处理器将产生缺页中断,通知操作系统,并将出现缺页的虚存地址(在CR2寄存器中)和缺页时访问虚存的模式一并传递给LINUX的缺页中断服务程序。

More Related