操作系统复习问题总结
1.为什么要有虚拟内存?
为了同时运行多个程序,就不能让软件直接去访问物理内存,防止数据覆盖,使每个进程都有自己的虚拟内存,每个进程都以为自己拥有整个内存,通过OS对进程的管理,让多个进程具有独立性,可以在同一机器上同时运行多个CPU。
进程通过CPU的MMU内存管理单元实现从虚拟到物理的映射。
操作系统通过内存分段和内存分页来管理,分段机制下,虚拟地址有段选择因子和段内偏移量组成
重点介绍一下分页,操作系统把内存切分成4KB的页来进行管理,当进程通过MMU去查页表的时候没查到就会触发缺页中断,进入内核,分配物理内存,更新页表,恢复进程的运行。
由于页的最小单位是4KB,最少也要分配一个页,如果程序大小不足一个页会出现浪费的问题。
OS非常聪明,当内存空间不够的时候,它会把正在运行的进程最近没有被使用的页换到磁盘里去,当需要访问的时候再从磁盘拿到内存,提高了内存的交换效率。
进一步的,可以在加载程序的时候,不必把整个程序加载到物理内存,当需要的时候触发缺页中断,从磁盘里拿就可以了。
分页的机制下通过页号和页偏移量进行从虚拟到物理的转化。页号拿到具体的页,偏移量得到具体的物理地址。
那么分页也有缺陷,比如虚拟地址是4GB,一个页是4KB,需要100万个页号,一个页号的大小是4字节,4GB的空间4MB拿来存储页号,一个进程都有自己独立的虚拟地址,当运行上1000个程序的时候就是4000MB,空间严重不足,所以提出解决方案,多级页表!
用2个页表来进行从虚拟到物理的映射,第一个页表有1024个页表项,每一个页表项目都映射着一个二级页表,每一个二级页表里面页有1024个页表项。那么有一个问题是你只不过是把100万个页表项放到二级页表里去了,这样做反而是4MB+4KB,反而增加了内存负担呢。当然如果4GB虚拟地址全部映射到物理内存上,确实是这样,但是一个进程往往不会为它分配这么大的内存,以OS的习惯来看,它常常会把经常没有使用的东西搞到磁盘里去,所以同样的,当某个页表项一定时间内都没有被访问,那就会把这个页表项换到磁盘里去,不再占用空间。
通过这种方式,我们是需要4KB的空间就实现了4GB虚拟空间的全覆盖,二级页表按需供给,大大降低了页表占用的内存大小,问题就此解决!
从页表的性质看,页表是负责把虚拟地址所有的虚拟地址转换成物理地址,所以页表必须保证对虚拟内存的全覆盖,如果某一个虚拟地址没有被覆盖,就无法通过虚拟地址转物理地址,达到查找的目的,不分页的页表需要100万个页表项来映射,二级分页后的页表只需要4KB,它们都覆盖了全部的虚拟内存,但是所占用的空间却是天差地别!
类似的,如果想要更小的空间覆盖整个虚拟内存,只需要再分级,就可以大大节省内存,不过相应的也会增加映射的时间。
2.TLB
如果说多级页表解决的是空间上的问题,那么时间上的问题谁来呢?
多级页表的转化节省了空间,但是也增加了寻址的时间,带来时间上的开销,那么我们就要提高效率。
程序是具有局部性的,即在某一段时间内,整个程序的执行仅限于程序中的某一部分,相应的,执行所访问的存储空间也局限于某个区域。
利用这一特性,我们只需要把常常访问的地址保存下来,减少映射的次数,就可以提高效率!
于是TLB快表应运而生,它在CPU的内部,访问速度极快。
CPU的MMU内存管理单元用来完成地址转换和TLB的访问与交互。
有了TLB后,在CPU寻址的时候,会先查TLB,如果没找到,才查常规的页表。
TLB的访问命中率极高,常常访问的页就那么几个。
总结:虚拟内存可以保持进程之间的独立性,互相运行不干扰。
虚拟内存可以让OS把一些不常用的内存换到磁盘里,让更大空间被使用
虚拟内存可以保证安全性,通过页表虚拟转物理的时候,会检查你的请求是否合法,不合法是不会让你去进行的,保证了安全性。
3.malloc是如何分配内存的
在Linux的内存布局中,有内核空间和用户空间,假如有4GB,内核空间就有1GB,用户空间3GB,内核空间的权限高,用户空间的权限低,所有进程的虚拟地址的内核空间都指向物理内存的同一块,用户空间从低地址到高地址分别是代码段,数据段,BSS段,堆,栈。
堆是用户自己创建的空间,用户来管理,栈是系统创建的,释放由OS来负责
malloc这个函数是库函数,并不是系统提供的系统调用函数,但是根据我们的知识,它的底层必定调用系统调用,因为申请空间是OS才能进行的,只有OS提供出来接口你才能通过这个接口去申请空间,malloc申请空间底层封装了2种系统调用,分别是brk,从堆分配内存,mmap,在文件映射区分配内存。方式一就是把brk指针向上移动,方法二就是直接在文件映射区拿一块内存出来。
从分配内存的方式看,brk就容易产生内存碎片,当你用brk申请一块空间后,大块空间释放后就会有一个巨大的缺口,然后当你再申请小块空间就会在释放掉的大块空间上申请,会把大块空间进行切分,这时候内存不连续,内存碎片问题就严重了而且brk只能将堆末尾空闲的空间还给OS,中间无论多大都无法还给OS,mmap就是直接给你一大块空间,释放的时候直接还回去就可以了,但是只有大块内存才值得用mmap,因为它的开销很大,如果小块内存频繁调用mmap你申请的那点空间还没有过程开销大。
malloc底层分配的不是物理内存,而是虚拟内存,你想,程序要空间,但是它现在不一定用,那么为了节省空间,OS肯定不会你要就立即给你啊,只有等到你要使用这块空间了,它才会给你,怎么给,查页表发现查不到,触发缺页中断,OS分配内存。
brk会申请一大块内存,mmap不会,频繁的小块内存调mmap会产生巨大的系统调用的开销,它按需求分配,由于这种特性大块内存找mmap,小块内存找brk。
而且malloc的brk也不是你要多少它就给申请多少,它是给你申请一大块空间,这次你申请,下次它还有它就不用调系统调用了,系统调用也是有消耗的。
还空间的时候brk不会还给操作系统,但是maap申请的空间会还给操作系统。
总结:小块内存用brk,大块内存用mmap,brk申请大块内存会产生巨大的内存碎片,因为brk是堆顶的指针,只要堆顶的内存不空闲,它就不会释放内存,mmap申请小块内存杀鸡用牛刀。
你看brk它如果在下面情况下申请30K的内存,它就必须再申请内存,中间的那30k就无法被使用,造成大量的内存碎片。
free的时候你只需要传一个指针,它就知道你释放多大的内存,这是因为你在申请的时候会拿16字节出来去储存它申请大多的空间。