Hugepages的前世今生 (六)

来源:互联网 发布:淘宝胡公子 编辑:程序博客网 时间:2024/05/21 22:21

在Linux系统中,有两种方式可以用来使用hugepages。一种是2.6内核就已经引入的Hugetlbfs虚拟文件系统,还有一种方式就是从2.6.38版本开始(RHEL 6)引入的THP(Transparent Hugepages),在现实世界中,hugetlbfs主要用于数据库,需要专门的进行配置以及应用程序的代码支持,而THP则可用于更广泛的应用程序,一切都交给操作系统来完成,也不再需要额外的配置,所以对于应用程序是透明的。

我们首先来看hugetlbfs。hugetlbfs是一个虚拟文件系统,它运行在内核层,不与磁盘空间相对应。如果需要使用hugetlbfs,则需要在编译Linux内核的时候在file system这个配置栏中勾选CONFIG_HUGETLB_PAGE和CONFIG_HUGETLBFS选项(如果勾选了CONFIG_HUGETLB_PAGE则CONFIG_HUGETLBFS会自动勾选)。内核编译完成并启动操作系统以后,将hugetlbfs文件系统挂载到特定的目录(不同的发行版目录可能不一样,Red Hat一般mount在/dev/hugepages),则hugetlbfs就可以访问了。完成以后可以通过cat /proc/filesystems | grep tlb来查看hugetlbfs是否已经开启。因为RHEL/OL提供二进制内核的rpm包,所以这些操作在RHEL/OL都不需要做。RHEL/OL上惟一需要做的就是在nr_hugepages中输入需要使用的hugepages的数量,例如:echo 25000 > /proc/sys/vm/nr_hugepages, 当然我们都知道/proc文件系统的内容只对当前生效,如果需要重启后依然有效则需要把vm.nr_hugepages = 25000这个条目加入到/etc/sysctl.conf内核配置文件中,然后重启操作系统或者使用sysctl -p 刷新内核参数配置才能生效。

如果一个程序要使用hugetlbfs,那么用户只能使用mmap 标准的SYSV函数shmget/ shmat来调用/访问它。如果使用mmap调用,则在mmap函数的第四个参数带入的标志位为MAP_HUGETLB, 具体实例请参考Linux内核文档:https://www.kernel.org/doc/Documentation/vm/map_hugetlb.c, 如果使用shmget接口来调用, 则需要则shmget的第三个参数的位置带入SHM_HUGETLB参数(shmat没有这个参数, 所以通过listener新建一个连接无法获知是否使用了hugepages。可参看shmat的man页面: http://linux.die.net/man/2/shmat)。具体实例请参考Linux内核文档:https://www.kernel.org/doc/Documentation/vm/hugepage-shm.c。可能有同学觉得知道这个对于一个dba毫无意义,因为可能我永远不会进行与hugepages开发相关的任何工作,但是实际上却不是这样的。Linux平台上根本无法从操作系统层面得知数据库是不是真正使用了hugepages。那么这个时候我们通过strace跟踪数据库的启动过程(启动以后无法看到)发现,其调用了shmget, 并且第三个参数为SHM_HUGETLB标志。这样我们就可以断定数据库使用了hugepages。例如: 如果kernel.shmmax为32G,通过strace跟踪就能看到类似的调用信息。(可以看出shmget的第二个参数为申请的内存大小,单位为Bytes)

1$ strace -fo strace.out sqlplus / as sysdba
2 
3SQL>startup nomount;
4 
5$grep HUGETLB strace.out | less
6 
7shmget(0xxxxxxxxx, 13625196544, IPC_CREAT|IPC_EXCL|SHM_HUGETLB|0660)
8 
9...

很多同学这个时候可能会争辩道: 我可以通过/proc/meminfo中的信息来确认是否使用了hugepages,我这里举例说明:以下是一个Exadata用户的hugepages信息:

1#cat /proc/meminfo | grep -i hugepage
2HugePages_Total:   13007
3HugePages_Free:    11813
4HugePages_Rsvd:     3372
5HugePages_Surp:        0
6Hugepagesize:       2048 kB

从上面的信息可以得出以下结论:

当前hugepages一共实际使用了13007-11813=1194个pages (不包括预留的), 预留了3372个pages。 永远不会使用达到了11813-3372=8841个pages。每个page大小为2M,也就是说有接近16.5G的hugepages内存被浪费了(因为hugepages无法swap,所以不能被其它程序所使用)。换句话说:当前系统使用中或者即将被使用的hugepages的总和为 HugePages_Total – HugePages_Free + HugePages_Rsvd=13007-11813+3372=4566个。
产生这样令人困惑的结果是因为用户为了省资源,在这台主机上配置了多个数据库实例,但是只有一个实例使用了hugepages。。。

之所以要提这个事情,是因为在Oracle 11.2.0.3版本以前,如果当前操作系统配置了hugepages,但是数据库实例设置的SGA大小如果比hugepages总的大小还要大,则SGA无法使用hugepages的内存,然后会去使用其它非hugepages的内存,并且在alert中也没有任何信息,进而导致系统内存占用率高,数据库性能非常糟糕。Kevin Closson在他的blog中有抱怨过这个问题,但是他却没说这实际上是一个Bug 12654172: EXHAUST THE AVAILABLE HUGEPAGES FOR ALLOCATING A REALM。 当然11.2.0.3以后的版本,会在alert日志提示hugepages没有使用的警告信息。

目前在Linux的主流版本,依然无法像其它平台那样使用操作系统工具来获取到数据库是否使用了hugepages。例如:在Solaris中可以使用pmap -s来查看,在AIX中可以使用vmstat来查看, 在RHEL/OL 6可以使用cat /proc/<pid>/smaps来查看。

To Be Continued…

原创粉丝点击