免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: tempname2
打印 上一主题 下一主题

[内存管理] 对内存管理的思考 [复制链接]

论坛徽章:
22
丑牛
日期:2014-08-15 14:32:0015-16赛季CBA联赛之同曦
日期:2017-12-14 15:28:14黑曼巴
日期:2017-08-10 08:14:342017金鸡报晓
日期:2017-02-08 10:39:42黑曼巴
日期:2016-11-15 15:48:38CU十四周年纪念徽章
日期:2016-11-09 13:19:1015-16赛季CBA联赛之同曦
日期:2016-04-08 18:00:03平安夜徽章
日期:2015-12-26 00:06:30程序设计版块每日发帖之星
日期:2015-12-03 06:20:002015七夕节徽章
日期:2015-08-21 11:06:17IT运维版块每日发帖之星
日期:2015-08-09 06:20:002015亚冠之吉达阿赫利
日期:2015-07-03 08:39:42
11 [报告]
发表于 2011-04-18 15:54 |只看该作者
感谢楼主分享,讲的分享好阿。只是字体太大了 影响阅读


Buddy System管理了内核的虚拟内存
这句话有问题吧,伙伴系统管理的是物理页,当要申请内存页的时候才向伙伴系统请求页

内核自己需要内存时,总是向Slab层申请
这句话也不对吧,slab用于缓存一些常用数据结构。

我的理解是,内核虽然映射了,但是要使用的时候还是必须要申请内存页。

下面是小弟随便写的一个例子,表明内核分配内存(注意返回地址在896M+0xc000 0000以内)
  1. dmesg:
  2. ......
  3. [ 9182.630688] ===
  4. [ 9182.630691] 0xc0c04000

  5. xxxx@laptop:~/work/module$ cat test.c
  6. #include <linux/module.h>
  7. #include <linux/types.h>
  8. #include <linux/fs.h>
  9. #include <linux/errno.h>
  10. #include <linux/mm.h>
  11. #include <linux/sched.h>
  12. #include <linux/init.h>
  13. #include <linux/cdev.h>
  14. #include <asm/io.h>
  15. #include <asm/system.h>
  16. #include <asm/uaccess.h>
  17. #include <linux/kernel.h>
  18. #include <linux/list.h>


  19. static int test_init(void)
  20. {
  21.         volatile int *p = kmalloc(1024, __GFP_DMA);
  22.         printk("===\n0x%x\n", (long)p);
  23.         kfree(p);
  24.        
  25.         return 0;
  26. }

  27. static void test_exit(void)
  28. {
  29. }

  30. MODULE_AUTHOR("xxxx");
  31. MODULE_LICENSE("Dual BSD/GPL");

  32. module_init(test_init);
  33. module_exit(test_exit);
复制代码

评分

参与人数 1可用积分 +6 收起 理由
Godbach + 6 感谢分享

查看全部评分

论坛徽章:
0
12 [报告]
发表于 2011-04-18 17:05 |只看该作者
感谢楼主分享,讲的分享好阿。只是字体太大了 影响阅读


Buddy System管理了内核的虚拟内存
这句话有问题吧,伙伴系统管理的是物理页,当要申请内存页的时候才向伙伴系统请求页
amarant 发表于 2011-04-18 15:54


我是这么说的。
严格来说,Buddy System在管理前896M物理内存时随便把内核的虚拟内存也管理了,因为两者是严格映射的。对进程来说,分配虚拟内存与分配物理内存是分开的步骤;对于内核来说,分配了物理内存就等于分配了虚拟内存。
   

SLAB那个,当时还没研究SLAB,那是瞎猜的。

论坛徽章:
0
13 [报告]
发表于 2011-04-24 23:35 |只看该作者
不错。我今天才看到啊。。。。

除了SLAB那块有点问题外,其他说的都挺好的。

再赞11楼

论坛徽章:
16
2015亚冠之吉达阿赫利
日期:2015-08-17 11:21:462015年迎新春徽章
日期:2015-03-04 09:58:11酉鸡
日期:2014-12-07 09:06:19水瓶座
日期:2014-11-04 14:23:29天秤座
日期:2014-03-02 08:57:52双鱼座
日期:2014-02-22 13:07:56午马
日期:2014-02-14 11:08:18双鱼座
日期:2014-02-13 11:09:37卯兔
日期:2014-02-06 15:10:34子鼠
日期:2014-01-20 14:48:19戌狗
日期:2013-12-19 09:37:46射手座
日期:2013-12-19 09:33:47
14 [报告]
发表于 2011-08-17 21:38 |只看该作者
不错。我今天才看到啊。。。。

除了SLAB那块有点问题外,其他说的都挺好的。

再赞11楼
accessory 发表于 2011-04-24 23:35



同感

论坛徽章:
0
15 [报告]
发表于 2011-11-25 11:19 |只看该作者
楼主将的很好!内核虚拟地址和物理地址的映射关系是固定的,如果能详细的讲一下这部分就好了。
最近在看这部分,但是迷迷糊糊的。

论坛徽章:
2
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:57:09
16 [报告]
发表于 2011-11-25 21:34 |只看该作者
回复 1# tempname2


    楼主写的不错啊,我刚开始看内存管理,看了您的文章有几个问题:
1.
在896M内存中,内核不能使用“被进程使用的内存”,即内核不能引用相应的页表里的内容,即内核不能使用相应的虚拟地址。

你这句话的意思是内核不能使用进程使用的内存对应的虚拟地址吗? 进程使用的虚拟地址空间是0~3G和内核使用的虚拟地址空间3G~4G没有交集啊,内核和进程没有虚拟地址空间的冲突吧,我感觉你是在讲的进程和内核共同访问同一物理内存(struct page)的冲突吧。
是不是假设模块物理内存被进程使用了,就会有标记,当内核访问这个内存时,就会发现这块物理内存是被映射过的,然后直接读取其虚拟地址,然后判断这个虚拟地址是否在>3G,如果不在就禁止访问。(我是猜的,还没来的及看代码)
2.
  1. 内核管理自己的虚拟内存时,利用分页,把“自己的虚拟内存固定到一块物理内存”
复制代码
个人认为,在内核中,虚拟地址到物理地址的映射不是通过分页来实现的,分页机制仅仅是给MMU用的,内核本身一般直接通过偏移来完成虚拟地址到物理地址的映射,不知道理解的对不对。
3. 我想问一下,通常情况下如果进程使用了某段物理内存,即映射了某段物理内存后是不允许内核再使用这段物理内存的,但是,mmap()是不是实现的就是用户空间和内核共享某段物理内存,此时,是不是就应该是内核和进程对这块物理内存映射到不同的虚拟地址上,希望请教下mmap的实现过程?

我刚开始看内存管理,希望楼主不吝指教!

论坛徽章:
2
CU十二周年纪念徽章
日期:2013-10-24 15:41:34处女座
日期:2013-12-27 22:22:41
17 [报告]
发表于 2011-11-25 22:39 |只看该作者
回复 16# liujunwei1234

>>你这句话的意思是内核不能使用进程使用的内存对应的虚拟地址吗? 进程使用的虚拟地址空间是0~3G和内核使用的虚拟地址空间3G~4G没有交集啊,内核和进程没有虚拟地址空间的冲突吧

我说的就是内核的虚拟地址。如果一个底端页被进程使用了,它就被映射了两次,一次被内核永久映射,一次被进程映射。内核自身的虚拟地址空间只存于3G以上,而其中大部分固定映射到了底端物理内存,也就是说,3G以上虚拟地址空间中前896M跟底端896M物理内存绑死了。进程从底端物理内存分出去多少页,内核3G~3G+896M虚拟地址空间中映射着那些页的虚拟地址也就跟着废掉了(如果不坚持固定映射,那一段虚拟地址就可以利用起来,映射其它地方的物理内存)。

内核虚拟地址空间与低端物理内存绑死了(或者说被牵制),其实这篇文章就是在说这个。我觉得,如果初看MM迷糊,大概也就是这里不清楚吧。知道这个就可以理解为什么内核不能使用896M以上的物理内存了:它的虚拟地址空间几乎已经用完了。


>>虚拟地址到物理地址的映射不是通过分页来实现的,分页机制仅仅是给MMU用的,内核本身一般直接通过偏移来完成虚拟地址到物理地址的映射

是的,内核的虚拟地址与物理地址仅存在偏移,但确实是利用分页来完成的。可以想象一下,表征3G以下虚拟地址的那部分页表可能是乱七八糟的映射,给定一个虚拟地址,如果不查页表就无从得知对应的物理地址。而3G~3G+896M虚拟地址空间是规律映射,也就是之前说的固定映射,而且永远不会变。给定虚拟地址,不用查页表也能知道其对应物理地址。

>>我想问一下,通常情况下如果进程使用了某段物理内存,即映射了某段物理内存后是不允许内核再使用这段物理内存的

分配内存的接口是统一的,即buddy system。如果给进程分配了物理内存页,后面不管是内核为自己申请内存还是为进程申请内存,都不可能可申请到那一页了。这里应该没有不允许,内核可以访问低端896M物理内存的任何位置,不管那段内存是不是被进程(或者之前已经被内核)使用。但是只有听从buddy system的指挥,只访问buddy system指示的可用内存,才可以保证系统秩序。

>>但是,mmap()是不是实现的就是用户空间和内核共享某段物理内存,此时,是不是就应该是内核和进程对这块物理内存映射到不同的虚拟地址上

这方面了解不多。mmap的一般用途是把处于内核态的page cache映射到进程用户态地址空间,避免一次内存拷贝。不过要了解整个cache的工作机制才比较好理解mmap的优势。

论坛徽章:
13
15-16赛季CBA联赛之同曦
日期:2016-01-28 19:52:032015亚冠之北京国安
日期:2015-10-07 14:28:19NBA常规赛纪念章
日期:2015-05-04 22:32:03处女座
日期:2015-01-15 19:45:44卯兔
日期:2014-10-28 16:17:14白羊座
日期:2014-05-24 15:10:46寅虎
日期:2014-05-10 09:50:35白羊座
日期:2014-03-12 20:52:17午马
日期:2014-03-01 08:37:27射手座
日期:2014-02-19 19:26:54子鼠
日期:2013-11-30 09:03:56狮子座
日期:2013-09-08 08:37:52
18 [报告]
发表于 2012-06-10 17:02 |只看该作者
本帖最后由 ulovko 于 2012-06-10 17:02 编辑

美文,多谢分享!

内核的虚拟内存被连续映射到最低端的物理内存。这是所有问题的开始。

为什么要把内核的虚拟地址空间连续地映射到物理内存最低端?这个根本不是个问题。开发人员或是出于效率的原因或是出于实现的原因,就是做了这样的设计。但这种设计却引发了很多令人困惑的问题。

假设我们使用32位版本内核,系统装有2G物理内存,下面所说的“内核的虚拟内存”是指内核前896M虚拟内存。

"内核将自己的虚拟内存连续映射到低端物理内存"到底会产生什么样的结果?是说低端那896M物理内存已被内核所占而不能另做它用?昨天讨论内存的帖子里给出了否定答案,看起来这是个很老的问题了。

书上往往会还有这样的话语:内核只能使用最低端896M物理;超过896M的内存称之为“高端内存”,无法被内核直接使用。初学者读到这里,总觉得哪里不对劲,却又道不出个所以然来。

既然内核已将自己的虚拟内存映射了,内核页表里entries是怎样的呢?在entry中,我们当前感兴趣的有两项:映射地址与Present标志位。映射地址嘛,肯定安排好了。Present标志位是不是1呢?不妨认为是1。这样一来,从内核的页表上来看,前896M的物理内存的的确确被内核所“占用”,只要内核引用相应的虚拟地址,内核就会访问到相应的物理地址,不会发生任何意外。但是,如前面所说,进程也是可以使用这块内存的。如果某个进程的某个虚拟地址映射到了前896M中的某一段,那么内核与进程都可通过各自的页表访问到这块内存。这样会不会发生冲突呢?

描述页帧的数据结构是page结构体,一个页帧是否空闲正是取决于对应page结构体中的引用数。那么内核映射的那896M物理内存是否空闲呢?除了内核代码及静态数据所占的物理页外,其它的理应全是空闲状态。也就是说《内存管理子系统》可以把这些内存分配出去。昨天帖子里说的“被内核映射了不等于被内核使用了”就是这么回事。内核页表里的内容是说,前896M物理内存处于一种Ready to use的状态,然而没有被内核使用的内存,进程也可以使用。在896M内存中,内核不能使用“被进程使用的内存”,即内核不能引用相应的页表里的内容,即内核不能使用相应的虚拟地址。

内核怎么能保证不引用某段虚拟地址呢?换句话说,内核是怎么使用自己的虚拟地址的呢?她怎么知道哪一段的虚拟地址已经《被自己使用了》或者《因为进程而不能使用》呢?总之,内核如何管理自己的虚拟地址呢?

先看内核如何管理进程的虚拟地址。

当进程需要内存时,内核首先会为进程分配一段虚拟地址,即所谓的Memory Region。由于进程虚拟地址的使用情况记录在vm_area_struct中,所有的vm_area_struct都按照顺序连接在一个链表上,因此寻找某大小的虚拟地址十分简单,扫描这个链表,碰到一块大于或等于[所申请内存大小]的连续地址,便用新的vm_area_struct记录下来,并插到链表的合适位置,这块虚拟地址便被标记为“已用”了。其实这就是操作系统理论里所讲的first fit。早期的Unix就是这样管理物理内存的,而这里用之管理进程的虚拟内存。内核为进程分配内存的步骤如下:
一、找到合适大小的虚拟地址段;
二、向{内存管理子系统}申请物理页帧;
三、在进程页表中建立两者的映射关系。

然而,内核为<自己>分配内存时,只有#第二步#。

没有第三步,是因为一开始的时候,内核页表已经做好了映射。那第一步呢?内核为什么没有像进程一样<<寻找合适大小的虚拟地址段>>这一步骤?

内核自己需要内存时,总是向Slab层申请(?)。需要创建新的数据结构,需要一个buffer时,Slab便为之分配一块连续的内存。内核不需要对这块内存做映射,因为最开始就映射好了。只要这块连续的物理内存在前896M之内,它本来就处于一种“预备被内核使用”的状态。从Slab里分出来后,它就“正式被内核使用”。那么,内核{申请内存时}得到[虚拟地址]是由什么决定的呢?内核没有“额外管理”虚拟内存,具体使用哪一块虚拟内存取决于Slab分配哪块物理内存。Slab分配哪块物理内存又取决了{内存管理子系统}。{内存管理子系统}={Buddy System}

结论是:Buddy System管理了内核的虚拟内存。

严格来说,Buddy System在管理前896M物理内存时随便把内核的虚拟内存也管理了,因为两者是严格映射的。对进程来说,分配虚拟内存与分配物理内存是分开的步骤;对于内核来说,分配了物理内存就等于分配了虚拟内存。内核为自己分配的内存究竟在虚拟内存的什么位置取决于分[配到的内存的页帧]在物理内存中的哪个位置。进程需要额外的结构体记录虚拟内存的使用情况;内核虚拟内存的使用情况就是前896M物理内存的使用情况。换句话说,一旦进程申请到了前896M物理内存中的某一块,就相当于侵占了内核的虚拟地址空间,就相当于内核的这段虚拟地址段不可使用。如果一块物理内存被Buddy System分给了内核,进程是没有机会再得到这块内存的,也就没有机会映射这块内存。如果一块物理内存被Buddy System分给了进程,虽然内核已经预先映射了这块内存,但永远得不到相应的虚拟地址,也自然引用不到相应的表项。这样就避免了两者间的冲突。当然,如果内核有BUG,指针使用不慎,引用了不该引用的虚拟地址,很容易破坏进程的内存。

这样一来,问题都解决了。前896M物理内存由内核与进程混用。但内核只能使用这896M物理内存(因为它的页表映射是固定的),而进程可以使用任何地方的物理内存(因为它的页表可以随意设置)。内核高128M留作其它用途,可以随意映射。内核不能直接使用的内存即“高端内存”。当内核的线性地址空间大于物理内存时,“高端”便不存在了。这种情况实际上有两种可能:一是内核线性地址空间太大,如使用64位系统;二是系统物理内存太小,如机器仅配有512M内存。两者都是{内核线性地址空间大于物理内存}的结果。

管理进程的虚拟内存时,利用分页,可以把虚拟地址随意映射到任何物理地址,这正是我们印象中“分页机制的常规用法”。相反的,内核管理自己的虚拟内存时,利用分页,把“自己的虚拟内存固定到一块物理内存”,反倒限制内核能使用的内存在物理内存中的位置,正好与我们印象中{分页机制的常规用法}相悖。初学者看到这块内容时,总有种“说不清道不明”的不协调感,大概就是这个原因吧。

论坛徽章:
0
19 [报告]
发表于 2012-06-20 22:28 |只看该作者
回复 1# tempname2


    很好!!!

论坛徽章:
0
20 [报告]
发表于 2012-06-21 10:08 |只看该作者
buddy 内存管理子系统里存在的物理页面到底是多少?是固定的吗
假如 最大的order是10,那么假如每个双项链表都是1个页块。也就是2^11 - 1 个页面。这才总共管理大约8M的物理页面。即使在三个区中DMA, NORMAL, HIGH_MEM中也才24M至多。
我想,buddy中的双向链表不应该是1个页块。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP