malloc用于运行时动态内存申请,堆中申请。根据虚拟内存的大小,可以申请的最大限度是3G(Linux 32位,虚拟内存空间共有4G大小,操作系统占用了1G的内存,剩下属于用户),申请的大小只和存储器的地址线和磁盘交换分区有关。虚拟内存空间,每个进程有足够的地址空间,所以只要交换分区即磁盘的大小允许,则就可以满足用户得到足够大的地址空间。
程序经过编译之后,会形成代码段,数据段,堆段,mmap段,栈段,操作系统区等。堆的增长方向是从低地址向高地址增长。经过malloc所申请的地址空间在堆区或者在mmap区域。
几个重要的系统函数mmap,mmuap,可以在mmap区按页申请空间。将传进去的文件描述符的文件从偏移处offset映射到所开辟的内存中,返回内存地址。而mmuap用来释放其所开辟的内存空间的。
在系统中,操作系统在维护着一个指针brk,这个指针记录着堆的位置,如果从堆区申请到内存大小length,则将brk返回,并将brk向高地址方向的偏移length字节。void*sbrk(int clr);函数用来申请地址,接受一个字节单位的值,可以是负值,成功就会返回该地址的首地址,失败会返回NULL。
malloc一次批发一大段的地址空间,然后对其进行管理。类似操作系统发展之中为进程管理内存使用的算法:首次适应算法,最佳算法,下次适应(NF)算法,还有如伙伴系统,哈希算法等等。将堆看成一大数组,对数组内存进行管理。申请内存就是从中获得某子数组空间。管理内存方法的好坏优劣如:1>来回的的申请释放产生尽可能少的外部碎片,使得来的任务需要较大的内存能得到分配 2>申请释放时候的效率,分配时候的效率,合并内存的效率。3>较少的使用辅助的内存,提高利用率。管理内存使用的有显示链表和隐式链表,隐式链表是在数组内部通过首部和偏移将一个数组形成一个链表,需要采用合适的结构体来组织管理数据。需要记录前后内存块的是否分配状态,还要保证分配的内存的对齐要添加一些填充字段,记录这个数组的长度便于跳到下一个数组的首部。如果所需要的内存块在查询链表之后能够得到,就进行分配,不然的话要重新进行sbrk来申请新的内存块。还可以采用显示链表来管理,即对结构体进行重新的安排,增加前驱后继等等。通常空间在使用时候的结构状态和未使用时的结构状态有所区别,空间的复用。在合并的时候,不一定释放内存空间的时候就开始进行合并,这会使得频繁的合并申请造成反应变慢。可以在申请失败之后对其进行合并,然后重新进行扫描。合并有几种情况:前驱释放,后继释放,前驱后继都被释放,前驱后继都没有被释放。
内存使用的时候还涉及到虚拟内存技术:虚拟地址空间,虚拟内存,交换分区
每个进程在进入内存运行开始,都会有自己的虚拟地址空间,各个进程的虚拟地址空间的大小都相同,即4G,其中的3G是属于用户使用的。在文件编译链接形成可执行文件的过程中其地址空间从0地址开始,然后在加载到内存地址时经过地址映射到某一具体的内存块处,使得虚拟地址和真实使用的内存地址之间形成了一种对应的关系,这个关系通过MMU单元进行地址的映射,其所需要的资源还有页表和段表,程序的内存是段页式存储的,分页有利用更加高效的利用内存空间,而分段是使得程序保证逻辑段存储。
交换分区就是内存在磁盘上的缓存空间。磁盘分为两部分,一部分存储文件,其需要的是存储密度高,而交换分区要与内存交往,所以要求其的速度较高,所以会分配连续的磁盘空间保证其请求速度。缺页中断之后根据一定的算法进行页面置换,选择内存中的一页面将其交换到页面缓存中,如果期间程序不再调用该页面,就会将其置换到磁盘中去。所以在操作系统中的中度调换中会有这两种源,磁盘空间的文件区,磁盘中的交换分区。
虚拟内存技术中间的关键是在地址的映射。如何将一个虚拟地址映射到一块真实的地址空间中去。采用虚拟内存地址有利于系统采取多任务工作,保证各进程作业期间不会互相干扰。
快表,是存储在比内存更快的高级别缓存中,存储的是页表项,用虚拟地址的页面号来搜索,如果搜索到了就会直接拿到物理页号,和偏移生成物理地址,只需要一次访问内存就可以从内存中拿到数据。不然的话需要三次来访问内存才能取到数据,访问一级页表,访问二级页表,访问数据。
虚拟地址空间中的分页是有硬件控制的,将内存地址分为一块一块的,其大小由系统设置,常见的大小4K。文件的存储也是以页分,可以高效的利用内存的地址空间。32位地址线的有4G的内存空间,对其进行采用的二级分页管理,第一页为页目录,其每个项目存放的第二页的地址,第二页存放的是物理地址的前22位。因为真实的物理地址和文件存储都是分页的,页面大小保持一致,所以其数据的在页中的偏移也是一致的。在CPU运行过程中产生了一个虚拟地址,在快表中进行查询,如果有就拿出在MMU中和偏移地址形成物理地址,不然就到内存中访问页表。如果该页还没有调入内存中,就会发生缺页异常,由专门的程序来将所需要的页面调入内存中。如果缺页异常过于频繁就会发生页面抖动。
虚拟内存的实现主要依赖于页面置换和请求调页。