趣谈Linux操作系统学习笔记-内存管理(21讲)
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了趣谈Linux操作系统学习笔记-内存管理(21讲),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含3263字,纯文字阅读大概需要5分钟。
内容图文
![趣谈Linux操作系统学习笔记-内存管理(21讲)](/upload/InfoBanner/zyjiaocheng/1332/6f932398d19449089a7e96d9e818ca53.jpg)
分段机制的原理
分段机制下的虚拟地址由两部分组成,段选择子和段内偏移
量
分段机制下的虚拟地址由两部分组成,段选择子和段内偏移量
段描述符
段寄存器的值是通过段描述符填充的。
GDT(全局描述符表) LDT(局部描述符表)
当我们执行类似MOV DS, AX
指令时,CPU会查表,根据AX的值来决定查找GDT还是LDT,查找表的什么位置,查出多少数据
段选择子:
段选择子是一个16位的段描述符,该描述符指向了定义该段的段描述符.
段选择子里面最重要的是段号,用作段表的索引。段表里面保存的是这个段 的基地址、段的界限和特权等级等
段权限检查
CPU分级
如何查看程序处于第几环?
CS寄存器的段选择子(共16bit)的后两个bit,表示的是CPL(Current Privilege Level):CPU当前特权等级
CS和SS中存储的段选择子后2个bit
DPL(Descriptor Privilege Level) 描述符特权等级
DPL存储在段描述符中,规定了访问该段所需要的特权级别是什么
举例说明:
如果AX指向的段DPL = 0 但当前程序的CPL = 3 这行指令是不会成功的
RPL(Request Privilege Level) 请求特权等级
举例说明:
mov ax, 0008 与 mov ax, 000B //段选择子
mov ds, ax //将段描述指向的是同一个段描述符,但RPL是不一样的
数据段的权限检查
参考如下代码:
比如当前程序处于0环, 也就是说CPL=0
mov ax, 000B //1011 RPL=3
mov ds, ax //ax指向的段描述符的DPL=0
数据段的权限检查:
CPL <= DPL 并且 RPL <= DPL(数值上的比较)
段偏移量
虚拟地址中的段内偏移量应该位于 0 和段界限之间。如果段内偏移量是合法的,就将段基地址加上段内偏移量得到物理内存地址
例如,我们将上面的虚拟空间分成以下 4 个段,用 0~3 来编号。每个段在段表中有一个项,在物理空间中,段的排列如下图的右边所示。
如果要访问段 2 中偏移量 600 的虚拟地址,我们可以计算出物理地址为,段 2 基地址 2000 + 偏移量 600 = 2600。
段表
1 #define GDT_ENTRY_INIT(flags, base, limit) { { { 2 .a = ((limit) & 0xffff) | (((base) & 0xffff) << 16), 3 .b = (((base) & 0xff0000) >> 16) | (((flags) & 0xf0ff) << 8) |4 ((limit) & 0xf0000) | ((base) & 0xff000000), 5 } } }
一个段表项由段基地址 base、段界限 limit,还有一些标识符组成
1 DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = { 2#ifdef CONFIG_X86_64 3 [GDT_ENTRY_KERNEL32_CS] = GDT_ENTRY_INIT(0xc09b, 0, 0xfffff), 4 [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(0xa09b, 0, 0xfffff), 5 [GDT_ENTRY_KERNEL_DS] = GDT_ENTRY_INIT(0xc093, 0, 0xfffff), 6 [GDT_ENTRY_DEFAULT_USER32_CS] = GDT_ENTRY_INIT(0xc0fb, 0, 0xfffff), 7 [GDT_ENTRY_DEFAULT_USER_DS] = GDT_ENTRY_INIT(0xc0f3, 0, 0xfffff), 8 [GDT_ENTRY_DEFAULT_USER_CS] = GDT_ENTRY_INIT(0xa0fb, 0, 0xfffff), 9#else10 [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(0xc09a, 0, 0xfffff), 11 [GDT_ENTRY_KERNEL_DS] = GDT_ENTRY_INIT(0xc092, 0, 0xfffff), 12 [GDT_ENTRY_DEFAULT_USER_CS] = GDT_ENTRY_INIT(0xc0fa, 0, 0xfffff), 13 [GDT_ENTRY_DEFAULT_USER_DS] = GDT_ENTRY_INIT(0xc0f2, 0, 0xfffff), 14...... 15#endif16} }; 17 EXPORT_PER_CPU_SYMBOL_GPL(gdt_page);
总结
分页机制本质上来说就是类似于linux文件系统的目录管理一样,页目录项和页表项相当于根目录和上级目录,
页内便宜量就是相对路径,
绝对路径就是整个32位地址,分布式存储系统也是采用的类似的机制,先用元数据存储前面的路径,
再用块内偏移定位到具体文件,感觉道理都差不多
内存分页
对于物理内存,操作系统把它分成一块一块大小相同的页,这样更方便管理,例如有的内存页面长时间不用了,可以暂时写到硬盘上,称为换出。一旦需要的时候,再加载进来,叫作换入。这样可以扩大可用物理内存的大小,提高物理内存的利用率
虚拟地址分为两部分,页号和页内偏移。页号作为页表的索引,页表包含物理页每页所在物理内存的基地址。这个基地址与页内偏移的组合就形成了物理内存地址
设计思路:
换入和换出都是以页为单位的。页面的大小一般为 4KB。为了能够定位和访问每个页,需要有个页表,保存每个页的起始地址,再加上在页内的偏移量,组成线性地址,就能对于内存中的每个位置进行访问了
页表的例子
上图缺陷:
页表中所有页表项必须提前建好,并且要求是连续的。如果不连续,就没有办法通过虚拟地址里面的页号找到对应的页表项了
改进1:
上图理解:
上面图中,我们假设只给这个进程分配了一个数据页。如果只使用页表,也需要完整的 1M 个页表项共 4M 的内存,但是如果使用了页目录,页目录需要 1K 个全部分配,占用内存 4K,但是里面只有一项使用了。到了页表项,只需要分配能够管理那个数据页的页表项页就可以了,也就是说,最多 4K,这样内存就节省多了
分页机制本质上来说就是类似于linux文件系统的目录管理一样,页目录项和页表项相当于根目录和上级目录,
页内变量就是相对路径,
绝对路径就是整个32位地址,分布式存储系统也是采用的类似的机制,先用元数据存储前面的路径,
再用块内偏移定位到具体文件,感觉道理都差不多
缺陷:
当然对于 64 位的系统,两级肯定不够了
改进2:
变成了四级目录: 分别是全局页目录项 PGD(Page Global Directory)、上层页目录项 PUD(Page Upper Directory)、中间页目录项 PMD(Page Middle Directory)和页表项 PTE(Page Table Entry)
总结:
我们可以把内存管理系统精细化为下面三件事情:
第一,虚拟内存空间的管理,将虚拟内存分成大小相等的页;
第二,物理内存的管理,将物理内存分成大小相等的页;
第三,内存映射,将虚拟内存也和物理内存也映射起来,并且在内存紧张的时候可以换出到硬盘中。
原文:https://www.cnblogs.com/mysky007/p/12315295.html
内容总结
以上是互联网集市为您收集整理的趣谈Linux操作系统学习笔记-内存管理(21讲)全部内容,希望文章能够帮你解决趣谈Linux操作系统学习笔记-内存管理(21讲)所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。