资讯专栏INFORMATION COLUMN

hugepage总结

nihao / 3310人阅读

摘要:大页的主要优点使用大页可以减少页表项数量,从而减少的概率,提升系统访存性能。

0.概述

本文总结了hugepage使用过程中的一些知识点,参考的内核版本是4.1.12
注:本文为原创,转载请注明出处

1.hugepage是什么,有什么用?

大页是相对传统4K小页而言的,一般来说常见的体系架构都会提供2种大页大小,比如常见的2M大页和1G大页。其实这两种大页size也分别对应PMD和PUD的一个页表项可以cover的物理内存大小。当然某些体系架构(如arm64)通过contiguous-tlb特性支持2种以上的大页。
大页的主要优点:使用大页可以减少页表项数量,从而减少TLB Miss的概率,提升系统访存性能。当然有利必有弊,使用大页降低了内存管理的粒度和灵活性,如果程序并不是对内存的使用量特别大,使用大页还可能造成内存的浪费。

2.如何使用hugepage

一般来说有三种方法:

使用mmap+MAP_HUGETLB直接匿名映射

使用shmget+SHM_HUGETLB属性分配

mount hugetlbfs后,在hugetlbfs下创建文件,然后通过mmap进行映射

注:上述方法使用的前提是,系统中加载了hugetlbfs,且存在空闲大页。可以通过cat /proc/meminfo查看是否有空闲内存。另外本质上匿名大页也是有对应的大页文件系统中的匿名文件,其实并不是真正的匿名页。

HugePages_Total: 10
HugePages_Free: 10
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 92308 kB
DirectMap2M: 1861632 kB

3.如何使能大页功能

一般来说有两种方法
1)在bootline中设置大页参数,这种方式最稳妥,因为大页本质上是连续的4K小页组成的,所以在系统启动时预留大页成功率最高。有三个参数,看名字就很清楚了
default_hugepagesz=1G hugepagesz=1G hugepages=4
如果没有定义default_hugepagesz,则大页size默认是2M

#define HPAGE_SHIFT        PMD_SHIFT
#define HPAGE_SIZE        (_AC(1,UL) << HPAGE_SHIFT)
#define HPAGE_MASK        (~(HPAGE_SIZE - 1))
#define HUGETLB_PAGE_ORDER    (HPAGE_SHIFT - PAGE_SHIFT)

static int __init hugetlb_init(void)
{
...

    if (!hugepages_supported())//不支持hugepage直接返回
        return 0;

    if (!size_to_hstate(default_hstate_size)) {//没有设置default_hugepagesz则默认2M
        default_hstate_size = HPAGE_SIZE;
        if (!size_to_hstate(default_hstate_size))
            hugetlb_add_hstate(HUGETLB_PAGE_ORDER);
    }

...
}

2)在系统启动后,通过echo 10 > /proc/sys/vm/nr_hugepages这种方式预留空闲大页

4.系统中是否可以同时存在多种大页类型

linux系统中是可以同时存在2种大页类型的,至于为什么是2种,个人理解是因为大页往往对应PMD和PUD两种级别的页表项所cover的物理内存大小。再大意义也不大了。可以通过下面的bootline设置两种大页类型
default_hugepagesz=1G hugepagesz=1G hugepages=4 hugepagesz=2M hugepages=4
内核启动的时候会解析bootline,然后把相应的信息存放在下面的结构体中

#define HUGE_MAX_HSTATE 2
struct hstate hstates[HUGE_MAX_HSTATE];

void __init hugetlb_add_hstate(unsigned order)
{
    struct hstate *h;
    unsigned long i;

    if (size_to_hstate(PAGE_SIZE << order)) {
        pr_warning("hugepagesz= specified twice, ignoring
");
        return;
    }
    BUG_ON(hugetlb_max_hstate >= HUGE_MAX_HSTATE);
    BUG_ON(order == 0);
    h = &hstates[hugetlb_max_hstate++];
    ...
    snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB",
                    huge_page_size(h)/1024);

    parsed_hstate = h; //解析hugepages参数时要使用这个变量
}


static __init int setup_hugepagesz(char *opt)
{
    unsigned long ps = memparse(opt, &opt);
    if (ps == PMD_SIZE) {//2M大页
        hugetlb_add_hstate(PMD_SHIFT - PAGE_SHIFT);
    } else if (ps == PUD_SIZE) {//1G大页
        hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT);
    } else {
        pr_err("hugepagesz: Unsupported page size %lu M
", ps >> 20);
        return 0;
    }
    return 1;
}
__setup("hugepagesz=", setup_hugepagesz);
5.如何使用指定的大页size

在使用mmap或者shmget获取大页时,如果直接使用MAP_HUGETLB或SHM_HUGETLB参数,则获取的大页size是2M

FILE:mmap.c
...
else if (flags & MAP_HUGETLB) {
        struct user_struct *user = NULL;
        struct hstate *hs;

        hs = hstate_sizelog((flags >> MAP_HUGE_SHIFT) & SHM_HUGE_MASK);
        if (!hs)
            return -EINVAL;
...

FILE:hugetlb.h
static inline struct hstate *hstate_sizelog(int page_size_log)
{
    if (!page_size_log)
        return &default_hstate;

    return size_to_hstate(1UL << page_size_log);
}

#define default_hstate (hstates[default_hstate_idx])

如果需要指定大页的size,则可以使用MAP或SHM flag的bit[31:26],这6个bit的取值范围是0~31,因此可以表示的页大小从0~2G,基本够用了。下面是SHM flag的代码,MAP flag与此类似

/* Bits [26:31] are reserved */

/*
 * When SHM_HUGETLB is set bits [26:31] encode the log2 of the huge page size.
 * This gives us 6 bits, which is enough until someone invents 128 bit address
 * spaces.
 *
 * Assume these are all power of twos.
 * When 0 use the default page size.
 */
#define SHM_HUGE_SHIFT  26
#define SHM_HUGE_MASK   0x3f
#define SHM_HUGE_2MB    (21 << SHM_HUGE_SHIFT)
#define SHM_HUGE_1GB    (30 << SHM_HUGE_SHIFT)

还有一种方法可以指定大页的size,那就是在mount时通过pagesize选项指定:
mount -t hugetlbfs -o pagesize=1G none /dev/hugepages1G

对应的代码如下所示:

FILE:fs/hugetlbfs/inode.c

        case Opt_pagesize: {
            unsigned long ps;
            ps = memparse(args[0].from, &rest);
            //根据输入的参数到hstates数组中查找匹配的数组项
            pconfig->hstate = size_to_hstate(ps);
            if (!pconfig->hstate) {
                //找不到匹配的大小就报错
                pr_err("Unsupported page size %lu MB
",
                    ps >> 20);
                return -EINVAL;
            }
            break;
        }

//最终hstate信息被保存在hugetlbfs的sb结构中
struct hugetlbfs_sb_info *sbinfo;
sb->s_fs_info = sbinfo;
sbinfo->hstate = config.hstate;

//后续hugetlbfs中的文件都可以通过file结构体关联到hstate结构体
static inline struct hstate *hstate_inode(struct inode *i)
{
    struct hugetlbfs_sb_info *hsb;
    hsb = HUGETLBFS_SB(i->i_sb);
    return hsb->hstate;
}

static inline struct hstate *hstate_file(struct file *f)
{
    return hstate_inode(file_inode(f));
}

//从而mmap就知道使用什么size的大页了,具体可以参见hugetlbfs_file_mmap函数
6.hugepage的缺页处理

hugepage在缺页时与普通小页的缺页流程是有区别的,具体流程如下
do_page_fault->__do_page_fault->handle_mm_fault->__handle_mm_fault->hugetlb_fault->hugetlb_no_page->alloc_huge_page
可以看出在__handle_mm_fault对于大页缺页直接调用了hugetlb_fault。另外多说一点,alloc_huge_page函数的参数如下所示

static struct page *alloc_huge_page(struct vm_area_struct *vma,
                    unsigned long addr, int avoid_reserve)
//通过下面的代码可以通过vma或者hstate
struct hstate *h = hstate_vma(vma);
//本质上还是调用了hstate_vma函数
static inline struct hstate *hstate_vma(struct vm_area_struct *vma)
{
    return hstate_file(vma->vm_file);
}
7.hugepage扩展

如果要同时支持2种以上的大页类型,比如2M,32M,1G,可以修改HUGE_MAX_HSTATE宏定义,同时也要修改
hugepage pte的底层操作接口,下面的例子是arm64 contiguous-tlb的patch,可供参考。
http://www.spinics.net/lists/...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/10163.html

相关文章

  • 禁用Transparent Huge Pages

    摘要:全文翻译于官方手册,已经尽力保持原版。但能力有限开机脚本使用与测试配置是否生效注意本页描述了如何在与上禁用,如需在其他的系统上执行禁用,请参阅相应文档。是一种内存管理机制,减少了使用时的寻址开销。你应该再主机上禁用以确保具备最佳性能。 全文翻译于MongoDB官方手册 ,已经尽力保持原版。但能力有限…… 开机脚本 使用tunded与ktune 测试配置是否生效 注意:本页描述了如何...

    mcterry 评论0 收藏0
  • php 开启opcache

    摘要:优化方案启用,启用非常简单在配置文件中加入使用以上的编译器来编译安装包,只有以上编译出的才会开启支持。开启支持,首先在系统中开启然后开启的。开启方法以为例通过命令分配个预留的大页内存。 优化方案 (1)启用Zend Opcache,启用Opcache非常简单, 在PHP.ini配置文件中加入: zend_extension=opcache.so opcache.enable=1 o...

    Developer 评论0 收藏0
  • php 开启opcache

    摘要:优化方案启用,启用非常简单在配置文件中加入使用以上的编译器来编译安装包,只有以上编译出的才会开启支持。开启支持,首先在系统中开启然后开启的。开启方法以为例通过命令分配个预留的大页内存。 优化方案 (1)启用Zend Opcache,启用Opcache非常简单, 在PHP.ini配置文件中加入: zend_extension=opcache.so opcache.enable=1 o...

    songze 评论0 收藏0
  • 转鸟哥建议:让PHP7达到最高性能的几个建议

    摘要:让达到最高性能的几个建议懒得排版了,伯乐在线链接原文出处惠新宸欢迎分享原创到伯乐头条已经发布了,作为十年来最大的版本升级,最大的性能升级,在多放的测试中都表现出很明显的性能提升,然而,为了让它能发挥出最大的性能,我还是有几件事想提醒下。 让 PHP7 达到最高性能的几个建议 懒得排版了,伯乐在线链接:http://blog.jobbole.com/95657/ 原文出处: 惠新宸(@L...

    dcr309duan 评论0 收藏0
  • Linux下如何确定进程是否使用了大内存

    摘要:如为配置了大内存,重启实例可在告警日志中观察是否实例使用了大内存,但是否有其他方式确定某个进程使用了大内存呢,下面以为例来说明如何从系统侧获知进程是否使用了大内存。 如为oracle配置了大内存,重启oracle实例可在alter告警日志中观察是否实例使用了大内存,但是否有其他方式确定某个进程使用了大内存呢,下面以oracle为例来说明如何从系统侧获知进程是否使用了大内存。 如orac...

    newtrek 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<