前言

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

目录

1、hugetlb模块

2、hugetlbfs模块

3、使用巨页

3.1、 mmap方式

3.2、共享内存方式


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大于当前池的页面数量,则更新,但是需要校验结果,因为内核一次性可能没有分配那么多。如果 N 为0,就全还给内核了。但是,必须等程序释放了内存,这些内存才会真正还给内核。如果程序还没释放,则这种需要释放还没释放的页面数,可以通过如下文件得知。
/sys/kernel/mm/hugepages/hugepages-2048kB/surplus_hugepages 或者 /proc/meminfo文件的 HugePages_Surp 字段
       另外,也可以通过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完成)。

3.1、 mmap方式

这种方式,需要先通过如下命令 mount 一个 hugetlbfs 文件系统,通过 pagesize 指定页面大小。
mount -t hugetlbfs none /wq/some_func/huge_mem_learn/file_path -o pagesize=2048K
这样的话,新挂载的文件系统,与页面大小为2048K的 hstate 相关联。接下来,在/wq/some_func/huge_mem_learn/file_path下面创建文件,然后打开文件并通过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)#define HUGE_SIZE   2048*1024   //2M
#define ALLOC_HUGE_SIZE (HUGE_SIZE*35) //申请大于当前空余页数,申请失败errno=12/* mmap方式可以使用指定尺寸的巨页池 */
int main(int argc, char *argv[])
{int ret = -1,fd = -1;char *huge_mem = NULL,*huge_mem2 = NULL;ret = system("mount -t hugetlbfs none /sbc/wq/some_func/huge_mem_learn/file_path -o pagesize=2048K");if (ret<0){ERR_DBG_PRINT("mount fail");return -1;}fd = open("/sbc/wq/some_func/huge_mem_learn/file_path/test", O_CREAT | O_RDWR, 0755);if (fd<0){ERR_DBG_PRINT("open fail");return -1;}   huge_mem = mmap(NULL, ALLOC_HUGE_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0);if (-1 == (int)(unsigned long)huge_mem){ERR_DBG_PRINT("first mmap fail");return -1;}printf("huge_mem1=%p\n",huge_mem);//映射两次,重新获得一个虚拟地址,两个虚拟地址指向同一块物理内存huge_mem2 = mmap(huge_mem, ALLOC_HUGE_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0);if (-1 == (int)(unsigned long)huge_mem2){ERR_DBG_PRINT("second mmap fail");return -1;}printf("huge_mem2=%p\n",huge_mem2);close(fd);//启动hugetlbfs后,即使test文件为NULL,也不会发生Bus errormemset(huge_mem, 0, ALLOC_HUGE_SIZE);memset(huge_mem2, 0, ALLOC_HUGE_SIZE);printf("huge_mem[0]=%c\n",huge_mem[0]);//验证指向同一块物理内存huge_mem2[0]='a';printf("huge_mem[0]=%c\n",huge_mem[0]);huge_mem2[0]='x';printf("huge_mem[0]=%c\n",huge_mem[0]);sleep(30);/* 下面代码一执行,内存就释放回巨页池了  */if (unlink("/sbc/wq/some_func/huge_mem_learn/file_path/test")==-1)ERR_DBG_PRINT("unlink fail");/*在同路径下执行,则卸载失败,显示信息如下:umount: /sbc/wq/some_func/huge_mem_learn/file_path: device is busy.(In some cases useful info about processes that usethe device is found by lsof(8) or fuser(1))*/ret = system("umount /sbc/wq/some_func/huge_mem_learn/file_path");if (ret < 0){ERR_DBG_PRINT("umount fail");return -1;}return 0;
}

3.2、共享内存方式

这种方式,不需要上面提到的 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 HUGE_SIZE   2048*1024   //2M
#define ALLOC_HUGE_SIZE (HUGE_SIZE*35) //申请大于当前空余页数,申请失败errno=12#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 = -1;int shm_id = -1;char *huge_mem = NULL;our_key = ftok("/wq/some_func/huge_mem_learn/src/huge_shmat_test.c", 6);if (-1 == (int)our_key){ERR_DBG_PRINT("ftok fail:");return -1;}shm_id = shmget(our_key, ALLOC_HUGE_SIZE, IPC_CREAT|IPC_EXCL|SHM_HUGETLB);if (-1 == shm_id){ERR_DBG_PRINT("shmget fail:");return -1;}huge_mem = shmat(shm_id, NULL, 0);if (-1 == (int)(unsigned long)huge_mem){ERR_DBG_PRINT("shmat fail:");return -1;} memset(huge_mem, 0, ALLOC_HUGE_SIZE);printf("huge alloc write ok \n");sleep(30);/* 下面代码一执行,内存就释放回巨页池了 */shmdt(huge_mem);if (shmctl(shm_id, IPC_RMID, NULL)==-1)ERR_DBG_PRINT("shmctl fail:");return 0;
}

Linux内核巨页代码解析和使用相关推荐

  1. Linux进程调用execve,linux内核系统调用函数do_execve()解析实例源码

    linux内核系统调用函数do_execve()解析,彻底解析内核调用用户空间代码入口函数do_execve() sys_execve() –> do_execve() /usr/src/lin ...

  2. linux内核中链表代码分析---list.h头文件分析(一)

    linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17:13:14 在学习数据结构时,有一个重要的知识点就是链表.对于链表的一些基本操作,它的最好学习资料就是内核中的li ...

  3. 【usb】linux内核USB键盘驱动解析--普通键值上报及转化

    一.概况 建议阅读前置文章[usb]linux内核USB键盘驱动解析–特殊键值上报及转化 以Linux5.10内核中USB键盘驱动为例进行解析:https://mirrors.edge.kernel. ...

  4. linux内核中链表代码分析---list.h头文件分析(二)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...

  5. 关于Linux内核中有多少代码是来自华人

    关于Linux内核中有多少代码是来自华人?统计是基于KPS(Kernel Patch Statistic), 日期是从2005年4月16日至今,也就是现在比较活跃的内核代码捐赠者,之前的捐赠者并没有包 ...

  6. Linux 内核 C 语言深度解析

    课程简介 在看一些 GNU 开源软件,或者阅读 Linux 内核.驱动源码时,相信大家和我一样,经常会遇到一些"看似熟悉",但一仔细分析又不是很懂的 C 语言"稀奇古怪& ...

  7. 嵌入式Linux内核移植相关代码分析(转)

    本文通过整理之前研发的一个项目(ARM7TDMI +uCLinux),分析内核启动过程及需要修改的文件,以供内核移植者参考.整理过程中也同时参考了众多网友的帖子,在此谢过.由于整理过程匆忙,难免 错误 ...

  8. 详细讲解一下Linux内核系统结构(图例解析)

    Linux系统一般有4个主要部分: 内核.shell.文件系统和应用程序.内核.shell和文件系统一起形成了基本的操作系统结构,它们使得用户可以运行程序.管理文件并使用系统.部分层次结构如图1-1所 ...

  9. Linux内核中oops 错误解析以及问题定位

    目录 一.oops输出解析 二.工具 1.objdump 2.gdb 3.addr2line 4.decodecode 5.faddr2line 文档最后有完整的oops输出文件,此处将输出分成多个小 ...

最新文章

  1. 【报名】杨植麟 :从学习的角度看NLP的现状与未来
  2. 为什么基类的析构函数要声明成虚函数
  3. 来晚了--SALTSTACK要弄起
  4. java的String构造对象的几种方法以及内存运行过程
  5. MySQL常用维护管理工具
  6. Android中多媒体处理【转】
  7. 解决PyCharm中报出 “Instance attribute xxx defined outside __init__“ 的警告
  8. Linux下内存问题检测神器 valgrind
  9. ubuntu wps缺少字体_WPS各版本
  10. WenLan-10亿参数!别只玩GPT,来看看人大中科院联手打造第一个大规模多模态中文预训练模型BriVL...
  11. mercurial使用_使用Mercurial在SQL数据库中对象更改的修订历史记录
  12. 深度之眼_Week2 编程作业1_梯度下降
  13. SQL 比较时间大小
  14. 我的世界手机版javaui材质包_传奇世界有元神怀旧版下载-传奇世界有元神怀旧版手机下载v1.0...
  15. 喜大普奔!Maya 2022来了?!
  16. 如何高效录制教学视频?
  17. netty系列之:请netty再爱UDT一次
  18. 为什么戏说php,戏说PHP的嵌套函数
  19. centos7.4/rehat7.0系统安装
  20. 六旋翼农用喷药、航拍功能无人机设计

热门文章

  1. 一、Swagger简介
  2. 给大家介绍一款开源的熟人/陌生人社交平台
  3. Quaternion kinematics for the error-state Kalman filter 文章理解
  4. SRGNN番外篇——我的死磕笔记
  5. Android 录屏原来可以这么优雅
  6. nexus 配置aliyun镜像
  7. Open-falcon配置邮箱报警
  8. 使用USBWriter做U盘启动盘后容量变小的解决办法
  9. Oracle到MySQL实时数据同步CloudCanal实战
  10. 液相色谱仪:堵塞的主要位置就是在色谱柱的前端