Linux内核巨页代码学习

来源:互联网 发布:java list length 编辑:程序博客网 时间:2024/05/17 01:15
巨页的实现,涉及到两个模块:hugetlb和hugetlbfs。


hugetlb相当于是huge page页面管理者,页面的分配及释放,都由此模块负责。
hugetlbfs则用于向用户提供一套基于文件系统的巨页使用界面,其下层功能的实现,则依赖于hugetlb。


1.    hugetlb模块

struct  hstate  hstates[HUGE_MAX_HSTATE];
上面定义了一个hstate数组,每个元素是一个巨页池。
不同的巨页池,其巨页尺寸是不一样的,例如2M的,4M的,1G的等等。
系统中可能会有多个巨页池,每一个池的巨页尺寸都是不一样的。
max_hstate标识当前有多少个hstate,即数组的前多少个元素是有效的。
默认的话,hugetlb_init中会创建一个hstate,其page size是默认大小,此hstate也就成了默认的hstate。
如果hugetlb.c被编译进内核,并且内核启动的时候,命令行参数中有hugepagesz=选项。
那么就会调用setup_hugepagesz进行处理,setup_hugepagesz应该会在hugetlb_init之前执行。
setup_hugepagesz中会调用hugetlb_add_hstate添加一个hstate,其pagesize大小为命令行参数中指定的大小。
每一个hstate,在/sys/kernel/mm/hugepages下面会有一个目录与之对应。通过读写此目录下的文件,即可实现对此hstate的各种属性的查看与修改。例如,可以查看或修改此hstate的页面数量。
例如,下面的命令将2M巨页池的页面数设置为N
echo  N  > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
如果N小于当前池中的页面数量,则会归还相应的页面给内核。
如果N为0,就全还给内核了。
但是,必须等程序释放了内存,这些内存才会真正还给内核。
如果程序还没释放,则这种需要释放还没释放的页面数,可以通过如下文件得知。
/sys/kernel/mm/hugepages/hugepages-2048kB/surplus_hugepages


另外,也可以通过sysctl命令,来修改巨页的配置。


copy_bootdata函数 (arch\x86\kernel\head64.c)中,会根据dpdk的设置来设置巨页相关的命令行参数。
当前系统具体是什么参数,可以在host上查看:cat /proc/cmdline
Hugepagesz参数规定了巨页尺寸。
Hugepages规定了页面数量。其页面在内核上电时,由hugetlb_nrpages_setup函数申请。


2.    hugetlbfs模块

hugetlbfs在加载时,会向内核注册hugetlbfs文件系统,并mount hugetlbfs文件系统到内核中,结果保存到hugetlbfs_vfsmount中。这个mount,没有使用什么参数,因此对应到默认的hstate。


3.    使用巨页

有两种方式,mmap方式和共享内存方式(shmget/shmat)。
但是,无论是通过哪种方式,最终都是通过对一个hugetlbfs类型的文件做内存映射而实现的(通过file->f_op->mmap完成)。


a.    mmap方式
这种方式,需要先通过如下命令mount一个hugetlbfs文件系统,通过pagesize指定页面大小。
mount -t hugetlbfs none /mnt/path/to/hugetlbfs -o pagesize=2048K
这样的话,新挂载的文件系统,与页面大小为2048K的hstate相关联。。
接下来,在/mnt/path/to/hugetlbfs下面创建文件,然后打开文件并通过mmap进行内存映射即可。


#include <sys/types.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <fcntl.h>#include <stdio.h>#include <string.h>#include <stdint.h>#include <errno.h>#include <sys/mman.h>#include <stdlib.h>#define    ERR_DBG_PRINT(fmt, args...) \    do \    { \        printf("ERR_DBG:%s(%d)-%s:\n"fmt": %s\n", __FILE__,__LINE__,__FUNCTION__,##args, strerror(errno)); \    } while (0)/* mmap方式可以使用指定尺寸的巨页池 */int main(int argc, char *argv[]){    int ret = system("mount -t hugetlbfs none /home/shared/sunmingbao/hugetlb_test -o pagesize=2048K");    int fd = open("/home/shared/sunmingbao/hugetlb_test/hehe.txt", O_CREAT | O_RDWR, 0755);    char *huge_mem = mmap(NULL, 2048*1024*5, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0);    if (ret<0)    {        ERR_DBG_PRINT("mount fail:");        return 0;    }    if (fd<0)    {        ERR_DBG_PRINT("open fail:");        return 0;    }    if (-1 == (int)(unsigned long)huge_mem)    {        ERR_DBG_PRINT("mmap fail:");        return 0;    }    printf("press any key to access huge page\n");    getchar();    memset(huge_mem, 0, 2048*1024*5);    printf("press any key to delete huge page\n");    getchar();    /* 下面代码一执行,内存就释放回巨页池了 */    if (unlink("/home/shared/sunmingbao/hugetlb_test/hehe.txt")==-1)        ERR_DBG_PRINT("unlink fail:");    return 0;}




b.    共享内存方式
这种方式,不需要上面提到的mount及创建文件操作。直接用shmget和shmat,即可使用巨页内存。
虽然用户没有mount及创建文件,但shmget内部还是创建了一个文件,并且是在上面提到的hugetlbfs_vfsmount挂载点下面。这样的话,就与mmap方式殊途同归了。hugetlbfs_vfsmount挂载点对应的是默认的hstate,因此所用巨页的页面大小也是默认的。


#include <sys/types.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <fcntl.h>#include <stdio.h>#include <string.h>#include <stdint.h>#include <errno.h>#include <sys/ipc.h>#include <sys/shm.h>#include <stdlib.h>#define    ERR_DBG_PRINT(fmt, args...) \    do \    { \        printf("ERR_DBG:%s(%d)-%s:\n"fmt": %s\n", __FILE__,__LINE__,__FUNCTION__,##args, strerror(errno)); \    } while (0)/* shmget方式只能使用默认尺寸的巨页池 */int main(int argc, char *argv[]){    key_t  our_key = ftok("/home/shared/sunmingbao/hello.c", 6);    int shm_id = shmget(our_key, 2048*1024*5, IPC_CREAT|IPC_EXCL|SHM_HUGETLB);    char *huge_mem = shmat(shm_id, NULL, 0);    if (-1 == (int)our_key)    {        ERR_DBG_PRINT("ftok fail:");        return 0;    }    if (-1 == shm_id)    {        ERR_DBG_PRINT("shmget fail:");        return 0;    }    if (-1 == (int)(unsigned long)huge_mem)    {        ERR_DBG_PRINT("shmat fail:");        return 0;    }    printf("press any key to access huge page\n");    getchar();    memset(huge_mem, 0, 2048*1024*5);    printf("<span style="font-family: Arial, Helvetica, sans-serif;">press any key to delete huge page</span>\n");    getchar();    /* 下面代码一执行,内存就释放回巨页池了 */    shmdt(huge_mem);    if (shmctl(shm_id, IPC_RMID, NULL)==-1)        ERR_DBG_PRINT("shmctl fail:");    return 0;}



0 0