malloc、mmap在Linux内核中的处理函数 如果已经看了今日头条中本系列文档的前面的几篇文章,应该就已经对malloc、mmap大致了解了,它们就是在堆中创建(或合并)所需虚拟地址的vma线性区,换句话说,就是达到进程地址空间中要有满足要求的vma,但不会给vma映射物理页(除非一定要求,即vma的flags标识了页锁定标志VM_LOCKED),这是linux的对用户进程物理页分配的推后原则,把握这个原则有助于分析malloc/mmap乃至理解linux的用户进程内存管理。 好文推荐: 2022年嵌入式开发想进互联网大厂,你技术过硬吗? 从事十年嵌入式转内核开发(23K到45K),给兄弟们的一些建议 腾讯首发Linux内核源码《嵌入式开发进阶笔记》差距差的不止一点点哦 库函数malloc在linux内核的实现是典型的匿名映射,关于匿名映射可以参考前面的缺页异常处理的文章,IPC方式中的共享内存也是匿名映射,绝大多数的mmap应用场合是文件映射,而内核中的处理,对于malloc的处理函数是do_brk,这是因为malloc操作的是进程地址空间的堆段,而函数do_brk就是针对堆段的处理,mmap包括IPC的共享内存都是由函数do_mmap处理;不论哪种处理其实操作的都是进程地址空间的线性区; 下面首先看下函数do_mmap,源码如下: 它的核心函数是do_mmap_pgoff,这里主要下do_mmap的参数情况:file: 如果新的线性区将要把一个文件映射到内存,则要用文件描述符file和文件偏移offset,如不需要,则file和offset不考虑都为空;addr: 指定从哪里开始查找空闲区间,一般都是NULL即由内核指定;len: 要求的线性地址空间长度;prot: 指定线性区下的页的访问权限;flag: 指定线性区域的其他标志; 初步有个印象即可,接下来函数do_mmap_pgoff,源码如下: 【文章福利】小编推荐自己的Linux内核技术交流群:【】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前100名进群领取,额外赠送一份价值699的内核资料包(含视频教程、电子书、实战项目及代码)
学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂ke.sigusoft.com/course/?flowToken=ke.sigusoft.com/course/?flowToken= 内核资料直通车:Linux内核源码技术学习路线+视频教程代码资料sigusoft.com/doc/DTkZRWXRFcWx1bWVxsigusoft.com/doc/DTkZRWXRFcWx1bWVx 这个函数由三部分组成: 1、 找到能否创建符合要求的vma,应该在哪里创建? 这部分主要通过函数get_unmapped_area实现,我们需要一段虚拟空间,范围是[addr,addr+len],用户进程一般不会指定addr(对应flags含义标志MAP_FIXED的情况),也就是由内核指定这个虚拟空间的首地址addr在哪里,在函数do_mmap_pgoff调用get_unmapped_area之前会预指定addr,通过调用函数round_hint_to_min实现,按我的理解这个预指定的值是宏CONFIG_DEFAULT_MMAP_MIN_ADDR 的值为4096(个人认为初始值是多少并不重要,因为后面会不断地找合适的值),然后用这个预指定的addr为参数调用函数get_unmapped_area,源码如下: 可见,函数get_unmapped_area实际是通过函数指针get_area实现,get_area有两种可能,如果是文件映射,并且该文件的file_operation定义了get_unmapped_area方法,那么使用它的get_unmapped_area方法实现定位虚拟区间,但我估计这样用的做法很少,以mmap使用较多的设备驱动来讲,多数设备驱动文件的file_operation没有定义get_unmapped_area方法,因为没有必要;所以一般都是用另一种方法,使用mm的get_unmapped_area方法,对于arm它是函数arch_get_unmapped_area,源码如下: 首先直接碰碰运气,看addr后面的vma(如果存在的话)与addr的距离是否够长,即大于长度len,如果可以的话,就直接返回addr即可; 往往不会这么容易成功,这就将进入循环查找流程,即标号full_search的部分,这里看到了mm的free_area_cache和cached_hole_size的用处了,它们就是标识从当前正在从哪里查找以及当前进程的vma之间最大空洞是多大,循环查找的过程就是让addr不断的蹦到一个vma的结尾处,把它与下一个vma的开始处的距离,和需要的长度len比较,当发现一个比len大的空洞时,即发现了可以用来创建新vma的地方了,返回addr,mm的free_area_cache就是标识每次是从哪里查找的,cached_hole_size的用处是不断更新为发现的最大空洞的值,这就利于今后再创建新vma;正常情况下返回的是找到的合适的addr; 回到函数get_unmapped_area,它将返回这个addr给函数do_mmap_pgoff; 2、 然后是确定vma线性区的flags,针对文件映射和匿名映射有所不同; 3、最后是实际创建新vma线性区,通过函数mmap_region实现,源码如下: 这个函数包括两部分内容: 1、 去除干扰:调用函数find_vma_prepare查找是否已有vma线性区包含addr了,如果有,调用函数do_munmap把这个vma干掉,函数find_vma_prepare的原理是:所有vma通过红黑树存储起来,通过当前红黑树把查找是否有包含addr的vma线性区,查找原理是利用红黑树的特性,后续会有关于红黑树的专题; 2、 创建映射:要注意,linux不希望vma和vma之间总有空洞,只要要新创建的vma的flags属性和它前面的或后面的vma相同,那么就可以合并成一个新的vma,这样做一来减少vma的个数,也就减少从slab的物理内存,二来减少虚拟空间的空洞的浪费;如果无法合并,那么也只好新建vma并对vma结构体初始化相关成员;根据vma是否有页锁定标志(VM_LOCKED),决定是否立即分配物理页; 3、 最后把该vma起始地址即addr返回; 最终do_mmap的返回值就是addr! 可见do_mmap完成的就是在本进程地址空间找到一段合适的虚拟地址空间,并把起始地址返回给用户进程,并未映射物理页(除非用户进程要求vma页锁定),这部分留给用户对其访问时产生的缺页异常处理。 下面再看函数do_brk,事实上do_brk和do_mmap几乎一样,因为它们的本质都是一样的;这里多说一下它由mm/mmap.c文件的系统调用SYSCALL_DEFINE1调用,系统调用的问题如前几篇文章一样,后续会有专题讨论它,这里直接看SYSCALL_DEFINE1函数源码: 首先看看函数SYSCALL_DEFINE1的参数,它的参数实际就是unsigned long brk,指定要配置的堆的地址,进程的mm的成员brk指定了当前进程的堆地址,在本函数中oldbrk和newbrk分别是当前的堆地址和希望的堆地址,如果newbrk更大,说明是free操作,即释放堆空间,将调用函数do_munmap; 如果不是释放操作,那就是申请堆空间,首先调用函数find_vma_intersection查看当前进程地址空间是否在brk处已经有vma并且长度足够,如已有则不用再申请可直接返回;否则还需调用函数do_brk扩大堆空间范围,实际上就是继续创建新的vma; 以上就是SYSCALL_DEFINE1的内容,下面看下函数do_brk,和do_mmap非常相似,源码如下: do_brk和do_mmap非常相似,首先确定在哪里适合创建新的vma,然后去除可能存在的已有vma的干扰,最后创建(或合并)vma,最后根据vma的flags是否有锁定标志(VM_LOCKED)决定是否立即分配物理页;
2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/80396.html