本文将先从进程内存管理出发,然后到内核中的内存管理
1.进程与内存?
所有的进程都必须占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等。不过进程对这些内存的管理方式因内存的用途而不一样,有些内存是事先分配的并且统一回收的,而有些却是按照需要动态分配和回收的。
对于任意一个进程都会涉及5个数据段。
代码段:代码段用来存放可执行文件的操作指令,也就是说它是可执行程序在内存中的镜像。代码段需要防止在运行时被非法修改,所以只准许读取操作,而不允许写入(修改)操作-它是不可写的。
数据段:数据段用来存放可执行文件已经初始换全局变量,即存放程序静态分配的变量和全局变量。
Bss段:程序未初始化的全局变量,在内存bss段全部置零。
堆:堆是存放进程运行时被动态分配的内存段,它的大小并不固定,可动态扩充或缩减。当进程调用malloc等函数分配内存时,新分配的内存被动态的添加到堆上,当利用free等函数释放的时候被释放的内存从堆上被剔除。
栈:栈是用户用来存放程序临时创建的局部变量,(不包括static)除此之外,在函数调用时,其参数也会压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放进栈中。
(1)进程如何组织这些区域?
上述几种内存区域中数据段、bss、堆通常是连续存储的-内存位置上是连续的。
(2)走进操作系统内核,看进程对内存如何分配?
从用户向内核看,内存经历了逻辑地址到线性地址到物理地址的形式转换。
逻辑地址到线性地址经过段机制转换,线性地址到物理地址经过页机制转换。(在Linux中将所有程序的段地址固定为0-4G,所以Linux中逻辑地址与线性地址的值是相等的)
2.进程内存空间?
Linux采用虚拟内存管理技术,使得每个进程拥有各自的进程地址空间,每个进程都拥有4G的虚拟线性地址,无法直接操作实际的物理内存地址。
(1)4G的空间被分成两部分-(3:1)用户空间和内核空间,用户空间时0-3G,内核为3G-4G。
进程一般情况下只能访问自己的用户空间的虚拟地址,只有在进行系统调用的时候才会去访问内核空间虚拟地址。
(2)进程发生切换,用户空间也会随之切换,但是内核空间因为由内核映射,所以不会改变。
(3)内核空间有自己独立的页表init_mm,用户进程有各自不同的页表。每个进程的用户空间时完全独立的。
3.进程内存管理?
进程内存管理的对象时进程线性空间上的内存映射,即虚拟内存区。进程虚拟空间是一个独立的连续的地址空间(是32位或64位的),虚拟空间被划分成大小可变的内存区域(必须是4K的倍数),这些区域按照可访问属性(可访问权限“可读或可写或可执行”)来划分。
查看进程的内存区域可以用命令:cat /proc/pid/maps.
Linux中对应进程内存区域的数据结构为:VM_area_struct,内核将每个内存区域作为一个单独的内存对象管理相应的操作也不相同。一般一个进程会需要多个内存区域来描述他的虚拟空间,这些虚拟空间被vm_area_struct以链表以及红黑树的形式组织起来,链表是为了方便遍历全部的结点,红黑树是为了方便对特定的内存区域的定位。进程的地址空间对应的描述结构是“内存描述符结构”,他表示进程的全部地址空间。
4.进程内存的回收与管理?
需要分配内存给进程的相关操作:malloc(),fork(),execv(),mmap(),brk()等函数,这个时候进程会申请虚拟内存,进程对内存的分配最终基本上都是由do_mmap()完成的(brk()被单独的当做系统调用使用。)do_mmap()将一个地址区间加入到了进程的地址空间中。
5.虚拟内存如何映射成物理内存?
当进程需要内存时得到的只是虚拟地址,但是处理器实际操作的是物理内存,所以需要将虚拟内存先转换成物理内存,实际的物理内存只有当进程真的去访问获得的虚拟空间时,“请求页机制”产生“缺页”异常,从而给分配实际的内存页面,并且建立相应的页表。
地址转换需要经虚拟地址分段,每段虚拟地址都指向页表,页表指向下一级别的页表或者最终的物理页面。每个进程都有自己的页表,进程描述符的pgd指向进程的页全局目录。
6.系统物理内存管理(页管理)?
Linux通过分页机制来管理物理内存,把整个内存划分成4K大小的页,为了方便管理系统一般在使用内存的时候会分配连续的内存块,为了尽量的减少不连续情况,采用伙伴系统来管理空闲页面,所以分配一般按照2的幂次方倍(2,4,8…512)页面来分配,内核中分配空闲页面基本函数是get_free_page。
物理页在系统中由页结构struct page描述,系统中所有的页面都存储在数组mem_map[]中,可以通过该数组找到系统中的每一页。
7.内核内存的使用以及如何避免内部分片和外部分片?
(1)内部分片
内核自身一般使用的内存比较小(存放文件描述符等操作小于一页),以页来分配不太适用。为了满足这种需求,Linux系统采用了slab分配器的技术,利用内存池的思想把内存片段看做对象,使用完后不释放而是放到内存池中,以便于下次再使用。
Slab并不是独立于伙伴系统存在的,而是在页面的基础上(把伙伴系统管理的空闲链表中的页分成许多小内存块来分配)。
Slab不仅用来存放内核专用的结构体,还被用来处理内核对小块内存的需求,一般通过slab分配器提供的kmalloc接口来完成申请。
(2)外部分片
伙伴关系和slab技术都是从防止“分片”的管理角度出发,但是尽管这样多次分配页面后,空闲内存依然会出现外部分片的现象,所以Linux借用Vmalloc来把一些不连续的内存块组合成为“一个看起来很大的内存块”,与用户空间相似内核也有一个名为init_mm的mm_struct结构来描述内核地址空间,其中页表项pdg包含了系统内核空间(3G-4G)的映射关系,所以vmalloc分配内核虚拟地址时必须更新内核页表(kmalloc,get_free_page因为分配的是连续的内存,所以不需要更新页表)。
内存管理
最新推荐文章于 2023-06-10 17:02:21 发布
本文深入解析了进程内存管理的基本概念,包括代码段、数据段、BSS段、堆和栈的作用与区别,以及Linux下进程如何组织这些区域。同时,探讨了虚拟内存与物理内存的映射机制,操作系统内核如何进行内存分配和回收,以及如何避免内外部分片问题。
1322

被折叠的 条评论
为什么被折叠?



