1. 段式(逻辑地址):
1)
逻辑地址是从程序的角度来看待内存,偏移位为0的地址即为段首地址。
2)
Linux不使用太多的分段,原因:
(1) 某些RISC机器对分段的支持不好;
(2)
Linux支持程序获得比物理内存大的多的内存空间,需要用到交换空间,而分段机制只能将整个段换入换出,代价太大,需要一种粒度更小的机制。
3)
Linux主要使用以下几种段:
-
- 内核代码段(Kernel Code Segment)
- 内核数据段(Kernel Data Segment)
- 用户代码段(User Code Segment)
- 用户数据段(User Data Segment)
- 任务状态段(Task State Segment)
- 其中,各个段的段基址都为0,所以每个逻辑地址寻址的是相同的0~4G的内存。而Linux是通过页式机制来管理内存的,映射流程为逻辑地址-->线性地址-->物理地址。由段基址为0可知,逻辑地址即为线性地址,多做一层映射只是为了兼容。
2. 页式(线性地址/虚拟内存):
1)
物理内存
是计算机
实际拥有的内存
,而
虚拟内存
是
根据CPU的寻址能力理论上可以拥有的内存
,Linux是通过
交换空间
来扩展超出物理内存的那部分内存。
2)
线性地址空间可达到4GB,这4GB一般按照3:1的比例进行分配,也就是说用户进程享有前3GB线性地址空间,而内核独享最后1GB线性地址空间。只有用户进程进行系统调用等时刻才可以访问内核地址空间;
3)每个进程都有一个页表。所以即使两个进程对同一个地址进行操作,也不会产生问题,它们有互补干涉的虚拟内存空间;
4)
内核有自己对应的页表。
用户空间对应进程,每当进程切换,用户地址空间就会跟着变化,而内核地址空间是不变的。
5)
用户空间动态申请内存时只获得线性地址的使用权,而并没有与实际物理内存对应,只有当用户空间真正操作申请的内存时,才会触发一次缺页异常,这时内核才会分配实际的物理内存给用户空间(写时拷贝);
6)
原理上来讲,Linux只需要为每个进程分配好所需数据结构,放到内存中,然后在调度进程的时候,切换寄存器cr3,cr3中存放着页目录的基址。在该基址的基础上,取线性地址的高10位作为页目录索引,取出该页目录项中的32位值作为页表的基址;在页表基址的基础上,取线性地址的中间10位作为页表索引,取出该页表项中的32位值;取出的32位值中,取前20位与线性地址的低12位相加,形成最终的物理地址。
3. 内部碎片与外部碎片:
1)
内部分片:系统为了满足一小段内存(连续)的需要,不得不分配一大块连续内存给它,从而造成了空间浪费,Linux通过slab来避免内部分片;
2) 外部分片:是指系统虽有足够的内存,但却是分散的碎片,无法满足对大块“连续内存”的需求,Linux通过伙伴系统来避免外部分片。
4. 内核中分配内存的3种方式:
1) get_free_page/get_free_pages:伙伴系统提供的接口,用于分配单个页面或连续指定的页面(2, 4, 8, ..., 512页);
2) kmalloc:Slab分配器提供的接口,
以字节为单位,
用于分配小于一页的内存,是get_free_page/get_free_pages方式的一个有效补充,使得内存分配的粒度更灵活。分配的内存在物理上是连续的;
3) vmalloc:也是分配以字节为单位的内存,只是无法保证所分配的内存地址在物理上连续,因此性能较差,所以该方式仅在不得已时才使用,如获得大块内存时。
REFERENCES:
1.
http://blog.csdn.net/yusiguyuan/article/details/9668363(CPU的页式管理那一节)
3.
http://www.kerneltravel.net/chenlj/lecture5.pdf