非连续的内存分配
一个程序的物理地址空间是非连续的。
硬件方案:分段和分页
能有效管理。
分段机制:中间需要映射机制,一维的逻辑地址空间中的堆,运行栈,程序数据等分别映射到物理地址空间中,通过分段机制实现。分段寻址方案:
CPU去执行程序中的每条指令,地址分开为段号和段内偏移,逻辑地址段号和物理地址段号之间的映射在段表中,查到base和limit,再去物理地址中查找。
分页机制:
分页的地址空间。页分为2的幂次分,一个程序的逻辑地址空间被划分大小相同的页。页号和业内偏移可以计算物理地址。
p页号,o偏移,pagetable存以页号索引的帧号信息。
逻辑地址空间会比物理地址空间大。
页表
怎么实现页表
逻辑地址空间有16bit,64k的逻辑地址空间,但是物理内存空间只有32k,每一个页大小都是一样的1024,(4,0),查页表,红字0表示不存在,表示内存访问异常;(3,1023),存在位为1,表示存在,00100,页帧号是4,页帧内的偏移跟页偏移是一样的都是1023.
缺点:访问一个内存单元需要2次内存访问:一次用于获取页表项,一次用于访问数据;
页表可能非常大,如64位机器如果每页1024个字节,那么一个页表的大小会是多少?2的64次方除以2的10次方,2的50次方。CPU放不下页表,如果放在内存中,开销也很大。
如何处理?
缓存;间接访问。把大空间拆成小空间
CPU中里面有一个TLB,并发查找,经常用到的页表项放到TLB。
一级,多级页表
malloc和mmap函数在分配内存时只是建立了进程虚拟地址空间,并没有分配虚拟内存对应的物理内存。当进程访问这些没有建立映射关系的虚拟内存时,处理器自动触发一个缺页异常,引发缺页中断。 缺页中断:缺页异常后将产生一个缺页中断,此时操作系统会根据页表中的外存地址在外存中找到所缺的一页,将其调入内存。当进程真正访问到此数据时,才引发缺页异常。
为什么要有虚拟内存
如果程序寻址用的都是物理地址。程序能寻址的范围是有限的,这取决于CPU的地址线条数。比如在32位平台下,寻址的范围是2^32也就是4G。并且这是固定的,且每次开启一个进程都给4G的物理内存.当有多个进程要执行的时候,都要给4G内存,这很快就分配完了,于是没有得到分配资源的进程就只能等待;而且内存时随机分配的,所以程序运行的地址也是不正确的。
所以虚拟内存可以:一个进程运行时都会得到4G的虚拟内存。这个虚拟内存你可以认为,每个进程都认为自己拥有4G的空间,这只是每个进程认为的,但是实际上,在虚拟内存对应的物理内存上,可能只对应的一点点的物理内存,实际用了多少内存,就会对应多少物理内存。
进程得到的这4G虚拟内存是一个连续的地址空间(这也只是进程认为),而实际上,它通常是被分隔成多个物理内存碎片,还有一部分存储在外部磁盘存储器上,在需要时进行数据交换。
堆溢出和栈溢出的情况
堆溢出:不断的创建对象,不释放,导致内存不足,造成oom;
栈溢出:递归较深或进入死循环,StackOverflflow Error
僵尸进程
当子进程先于父进程退出,但是父进程没有调用wait/waitpid回收子进程的资源,则子进程变成僵尸进程。
一般,为了防止产生僵尸进程,在fork子进程之后我们都要及时使用wait系统调用;同时,当子进程退出的时候,内核都会给父进程一个SIGCHLD信号,所以我们可以建立一个捕获SIGCHLD信号的信号处理函数,在函数体中调用wait(或waitpid),就可以清理退出的子进程以达到防止僵尸进程