系统相关
首页 > 系统相关> > 内存管理(4): FAQs

内存管理(4): FAQs

作者:互联网

4       FAQs

4.1 Flags:

与内存管理系统相关的各种flags汇总如下:

         每个物理page有自己的flags, 定义在struct page -> unsigned long flags; 详情见2.2.2节的《page》

         每个内存块(多个page组成一个内存块)有自己的pageblock_flags, 定义在struct zone -> unsigned long *pageblock_flags详情见2.4.3节的《辅助函数与变量》

         在申请内存时, 需要使用gfp flags, 详情见2.4.5节的《分配掩码GFP_XXX》

         kmem_cache_create函数需要用到SLAB flags, 详见2.5.3节《APIs》中关于kmem_cache_create的介绍

         页表只需存储物理页的地址, 因此页表每一项(pte)的0 - PAGE_SHIFT位都是空闲的, 这些空闲位可以用来做存储保护用, 详见3.1.1节《pte flags》

         vmalloc区域的子区域用vm_struct表示, 每个vm_struct都有自己的flags, 详见3.2.4节《vm_struct》

         内存映射区的每个子区域用vm_area_struct表示, 每个vm_area_struct都有自己的vm_flags, 详见3.3.4节《子区域(vm_area_struct)》

4.2 何时分配物理页帧& 创建页表

         创建新的内存映射时, 除非指定了MAP_LOCKED标志, 否则不会向伙伴系统申请物理页帧, 也不好创建页表.

只有当访问了某个虚拟地址, 发现它没有对应的物理页面, 会触发缺页异常. 此时缺页异常处理程序才会向伙伴系统申请物理页帧并建立页表.

         如果创建映射时指定了MAP_LOCKED标志, 或者是在堆扩大的时候, 都会通过mm_populate对每个虚拟页面触发缺页异常, 然后缺页处理程序会开始运行. 也就是说这两个操作成功返回时, 物理页帧和页表都准备好了.

4.3 CONFIG_HIGHMEM和物理内存大小的关系

如果物理内存很小, 在《2.3.3 确定低端/高端内存的分界值》时, 会判断系统没有高端内存, 也就是说不管是否使能CONFIG_HIGHMEM, 系统都没有高端内存.

 

如果物理内存很大, 但是没有使能CONFIG_HIGHMEM, 内核就只能使用线性映射的物理内存, 无法使用多余的物理内存.

这种个情况下, 《2.3.3 确定低端/高端内存的分界值》时, 内核会打印提示信息, 提醒用户使能CONFIG_HIGHMEM.

 

如果物理内存很大, 而且使能了CONFIG_HIGHMEM, 则系统就可以使用高端内存了. 一般用”HIGHMEM”简称这种情况.

4.4 vmalloc与HIGHMEM有关系吗?

不管有没有HIGHMEM, vmalloc都可以使用: 当存在HIGHMEM时, vmalloc优先从ZONE_HIGHMEM分配物理页帧; 否则vmalloc从ZONE_NORMAL分配物理页帧.

 

不过PKMAP域与CONFIG_HIGHMEM有关系, 只有当系统使能了CONFIG_HIGHMEM时, 才可以使用PKMAP域, 详见《3.2.3 PKMAP & FIXADDR》.

4.5 vmalloc & buddyinfo

那如果我用vmalloc申请一块内存, 会对buddyinfo的输出产生影响吗? 做个试验吧, 在BBB的板子上, 编写个ko, init阶段用vmalloc申请8个page, exit阶段释放这8个page. 观察期间buddyinfo的变化.

 

实例代码如下:

    vmalloc_ptr =vmalloc(PAGE_SIZE *8);

    if(vmalloc_ptr !=NULL){

        printk(KERN_ALERT "vmalloc_ptr %p\n", vmalloc_ptr);

        *(unsignedint*)vmalloc_ptr =88;

        printk(KERN_ALERT "value of vmalloc_ptr %d\n",*(unsignedint*)vmalloc_ptr);

    }else{

        printk(KERN_ALERT "vmalloc alloc failed\n");

    }

 

运行结果如下:

结论是不会影响buddyinfo的输出, 原因是vmalloc是一页一页的申请物理页面的, 所以会从pcp list里面获取页面, 只有当pcp list的页面不足时. pcp list才会向伙伴系统申请页面, 此时才会影响buddyinfo的输出. 详情见《2.4.6 buffered_rmqueue》

4.6用户进程的页表是否会包含内核虚拟地址空间?

在ARM32上, 当fork进程时, 会把内核页表的一级目录copy到用户进程页表task_struct->mm_struct->pgd中. 负责copy的代码是mm_alloc -> mm_init -> mm_alloc_pgd-> pgd_alloc (for arm32) :

 

但在ARM64上, 用户进程的页表是存放在ttbr0_el1, 内核态页表是存放在ttbr1_el1, 因此无需copy.

arch/arm64/mm/pgd.c

4.7copy_{to,from}_user()的思考

http://www.wowotech.net/memory_management/454.html , 这篇文档很好的讨论了这个问题. 个人的一点总结如下:

copy_xxx_user与memcpy相比, 提供了如下额外的功能:

         它会调用access_ok, 确保传递给内核的用户空间的地址是属于当前进程的. 否则可能利用这个漏洞获取root权限.

         如果传递给内核的用户空间地址是一个非法地址(用户空间的vm_area域不包含该地址), 内核不会oops, 而是通过.fixup和__ex_table两个段的帮助, 使得内核可以正常的返回用户空间. 如果用memcpy的话, 这种情况内核会直接oops.

         在使能CONFIG_ARM64_SW_TTBR0_PAN或者CONFIG_ARM64_PAN(硬件支持的情况下才有效)的时候,当切换到内核空间时, 会通过修改页表使得内核无法访问用户空间的地址. 此时我们只能使用copy_{to,from}_user()这种接口(copy_xxx_user在其入口处会暂时恢复用户空间页表, 在其退出时再次把用户空间页表设为无效值)。

 

如果不考虑上述3点区别, 在内核空间访问下述两种用户空间地址时, copy_xxx_user与memcpy都能正常工作:

         一个有效的用户空间地址, 并且已经映射到了物理内存

         一个有效的用户空间地址, 但还未建立映射. 针对该地址, 无论是内核态还是用户态访问它, page fault的流程几乎一样,都会帮助我们申请物理内存并创建映射关系。

 

 

标签:内核,管理,HIGHMEM,FAQs,页表,物理,vmalloc,内存
来源: https://www.cnblogs.com/jliuxin/p/14129405.html