Kernel Exploring
  • 前言
  • 支持
  • 老司机带你探索内核编译系统
    • 编译出你的第一个内核
    • 内核编译中的小目标
    • 可能是kbuild中最直接的小目标 – help
    • 使用了一个kbuild函数的目标 – cscope
    • 内核中单个.o文件的编译过程
    • 根目录vmlinux的编译过程
    • 启动镜像bzImage的前世今生
    • setup.bin的诞生记
    • 真假vmlinux–由vmlinux.bin揭开的秘密
    • bzImage的全貌
    • kbuild系统浅析
  • 启动时的小秘密
    • INIT_CALLS的秘密
    • 内核参数
  • 内核加载全流程
    • bootloader如何加载bzImage
    • 内核压缩与解压
    • 内核加载的几个阶段
    • 保护模式内核代码赏析
  • 内存管理
    • 内核页表成长记
      • 未解压时的内核页表
      • 内核早期的页表
      • cleanup_highmap之后的页表
      • 映射完整物理地址
      • 启用init_level4_pgt
    • 自底而上话内存
      • e820从硬件获取内存分布
      • 原始内存分配器--memblock
      • 页分配器
        • 寻找页结构体的位置
        • 眼花的页结构体
        • Node-Zone-Page
        • 传说的伙伴系统
        • Compound Page
        • GFP的功效
        • 页分配器的用户们
      • slub分配器
        • slub的理念
        • 图解slub
      • 内存管理的不同粒度
      • 挑战和进化
        • 扩展性的设计和实现
        • 减少竞争 per_cpu_pageset
        • 海量内存
        • 延迟初始化
        • 内存热插拔
        • 连续内存分配器
    • 虚拟内存空间
      • 页表和缺页中断
      • 虚拟地址空间的管家--vma
      • 匿名反向映射的前世今生
      • 图解匿名反向映射
      • THP和mapcount之间的恩恩怨怨
      • 透明大页的玄机
      • NUMA策略
      • numa balance
      • 老版vma
    • 内存的回收再利用
      • 水线
      • Big Picture
      • 手动触发回收
      • Page Fram Reclaim Algorithm
      • swapfile原理使用和演进
    • 内存隔离
      • memcg初始化
      • 限制memcg大小
      • 对memcg记账
    • 通用
      • 常用全局变量
      • 常用转换
    • 测试
      • 功能测试
      • 性能测试
  • 中断和异常
    • 从IDT开始
    • 中断?异常?有什么区别
    • 系统调用的实现
    • 异常向量表的设置
    • 中断向量和中断函数
    • APIC
    • 时钟中断
    • 软中断
    • 中断、软中断、抢占和多处理器
  • 设备模型
    • 总线
    • 驱动
    • 设备
    • 绑定
  • nvdimm初探
    • 使用手册
    • 上帝视角
    • nvdimm_bus
    • nvdimm
    • nd_region
    • nd_namespace_X
    • nd_dax
      • dev_dax
  • KVM
    • 内存虚拟化
      • Qemu内存模型
      • KVM内存管理
  • cgroup
    • 使用cgroup控制进程cpu和内存
    • cgroup文件系统
    • cgroup层次结构
    • cgroup和进程的关联
    • cgroup数据统计
  • 同步机制
    • 内存屏障
    • RCU
  • Trace/Profie/Debug
    • ftrace的使用
    • 探秘ftrace
    • 内核热补丁的黑科技
    • eBPF初探
    • TraceEvent
    • Drgn
  • 内核中的数据结构
    • 双链表
    • 优先级队列
    • 哈希表
    • xarray
    • B树
    • Maple Tree
    • Interval Tree
  • Tools
    • 发补丁
    • 检查文件变化
    • selftest
      • 构建过程
  • Good To Read
    • 内核自带文档
    • 内存相关
    • 下载社区邮件
Powered by GitBook
On this page
  • 以page_type分类的类型
  • PageTable
  • PageBuddy
  • PageGuard
  • 以flags分类的类型
  • PageHuge
  • PageLRU
  • PageSlab()
  • PageTransHuge
  • PageSwapCache
  • PageSwapBacked

Was this helpful?

  1. 内存管理
  2. 自底而上话内存
  3. 页分配器

页分配器的用户们

内存从页分配器(buddy系统)中分配出去后,就散落在了系统的各个角落。对于一个特定的pfn或者page,怎么知道这个页是分配给了谁,用在了哪里是内核对内存管理的一个任务。

比如函数memory_failure中就对不同用途的页做了不同的出错处理。下面我将列出我所知道的页分配器的“知名”用户们。

对于页用途的标示,在内核中又分成两类途径:

  • page->flags

  • page->page_type

不知道为什么要分成两个字段来做区分,可能是用满了吧。

鉴于用page_type来区分的类型较少,我们就先介绍page_type中的类型。

以page_type分类的类型

page_type支持的类型一共就五种

#define PG_buddy	0x00000080
#define PG_offline	0x00000100
#define PG_kmemcg	0x00000200
#define PG_table	0x00000400
#define PG_guard	0x00000800

对应的操作通过PAGE_TYPE_OPS来定义,在使用中也是用__SetPageXXX和__ClearPageXXX来判断。

PageTable

Marks pages in use as page tables. 也就是这个页我们是用来做页表的。

设置这个类型的地方只有天字唯一一个地方:

__pte_alloc_one
    pgtable_pte_page_ctor
        __SetPageTable(page)

因为页表,至少在x86上占一个页面,所以没有compound page的可能。

PageBuddy

这个用来判断一个页是是否在buddy系统中,如果在的话,说明还没有分配出去。

但是有意思的是,并不是所有在buddy系统中的空闲页都会设置这个类型。而是只有在整个页的page[0]对应的page_type才会被设置。所以函数is_free_buddy_page()就这样诞生了。

设置这个类型的地方也是只有天字唯一一个地方:

__free_one_page()
    set_page_order()
        set_page_private(page, order);
  	    __SetPageBuddy(page);

因为只有对头部的page设置这个值,所以这个判断的作用需要进一步考量。

PageGuard

这是一个在debug情况下才用到的判断。

调用的地方也只有一个:

expand()
    set_page_guard()
        __SetPageGuard()

这样被分片的页就不能再被分配了。也不知道这是什么调试技巧。

以flags分类的类型

PageHuge

这个判断用来确认对应的页是不是hugetlbfs中分配的。其中有个重要的依据就是compound_dtor是不是HUGETLB_PAGE_DTOR。

这个设置的工作在prep_new_huge_page()函数中完成。

从PageHuge的代码中可以看到,被判断的为真的页必须是compound page。这个设置的过程有点复杂。

比如在alloc_fresh_huge_page()函数中,分成两种情况。

  • alloc_gigantic_page

  • alloc_buddy_huge_page

后者alloc_buddy_huge_page函数中,明确设置了__GFP_COMP。 但是在alloc_gigantic_page中,并没有用通用的方式设置compound page。而是在prep_compound_gigantic_page中设置了对应的Head/Tail。这一点颇为有趣。

PageLRU

这个判断用来标示指定的页是不是在lru链表上。在pagevec cache上的页,也不会设置这个标示。

咨询了一下huangying,一共有四种情况会把page放到lru链表上。

  • anonymous page

  • file backend pages

  • page cache(read/write through syscall)

  • shmem

按照我的理解,添加到lru链表的一个重要来源如下:

__lru_cache_add()
    __pagevec_lru_add()
        __pagevec_lru_add_fn()

而__lru_cache_add()函数的调用者有三种情况:

  • lru_cache_add_anon

  • lru_cache_add_file

  • lru_cache_add

调用的地方较多,其中有一个地方的流程是:

handle_pte_fault
    do_anonymous_page
        page = alloc_zeroed_user_highpage_movable()
        lru_cache_add_active_or_unevictable()
            lru_cache_add()

从这一点上至少可以证明普通进程中的映射的页会添加到lru链表中。同样看了一眼hugepage的流程,hugepage的页面也会添加到lru链表中。

PageSlab()

对于给slab分配器使用的内存,只要这个用来判断就知道了。

设置这个bit的位置只有一个,在函数allocate_slab()中。从页分配器中获得了page后,则会做上相应的标记。

另外需要说明的是,slab中分配到的页只要是高阶的那就是compound page。因为在函数calculate_size中会做如下操作。

if (order)
  s->allocflags |= __GFP_COMP;

PageTransHuge

这个函数说是用来判断Transparent Huge Page的,但说实话我没有看懂它是怎么做到的。因为它其实只是判断了是不是PageHead。

那我们还是来看看Transparent Huge Page是怎么个分配的吧。

Transparent Huge Page的分配发生在缺页中断,其中有两个地方可能分配create_huge_pud和create_huge_pmd。

我们以匿名页表的pmd创建为例。

do_huge_pmd_anonymous_page
    gfp = alloc_hugepage_direct_gfpmask(vma);
        return GFP_TRANSHUGE or GFP_TRANSHUGE_LIGHT
    page = alloc_hugepage_vma(gfp, vma, haddr, HPAGE_PMD_ORDER);
    prep_transhuge_page(page);
        set_compound_page_dtor(page, TRANSHUGE_PAGE_DTOR)

其中值得注意的是gfp的计算和prep_transhuge_page。

alloc_hugepage_direct_gfpmask返回的gfp都包含了__GFP_COMP。这一点保证了得到的页一定是compound page。 prep_transhuge_page中设置了compound_dtor。个人感觉用这个来做判断更加准确。

PageSwapCache

有几个地方设置,其中之一在函数add_to_swap_cache()中。

  shrink_page_list
    add_to_swap(page)
      add_to_swap_cache(page, entry, gfp)
        SetPageSwapCache(page)

感觉这条线还是从kswapd下来的。

PageSwapBacked

这个也有几个地方可以设置,其中一个在缺页中断的do_swap_page()。

  handle_pte_fault
    if (!pte_present(vmf->orig_pte))
      return do_swap_page(vmf);
        __SetPageSwapBacked(page)

感觉意思是发生缺页中断,如果这个页有在swap中了,那就标记这个位。

PreviousGFP的功效Nextslub分配器

Last updated 3 years ago

Was this helpful?