本文翻译自:http://linuxgazette.net/155/krishnakumar.html

 

介绍

从内存管理的角度,所有的物理内存都被划分为一个个的frame,而虚拟内存则被划分为一个个的page。内存管理单元的一个任务就是维护这两者之间的一个映射关系。

这个映射关系通常是保存在一个“页表”中的,但是通常,该表的查询比较耗时间,因此为了提高查询过程,系统中引入了一个叫做(TLB)的cache。

引入TLB后,物理内存页和虚拟内存页的映射首先通过查询TLB来完成,如果TLB中没有相关的对应关系,我们称此时发生了一个 TLB miss,此时内存管理单元将通过进一步查询页表来完成映射请求,因此带来了比较打的开销。

一般的X86机器上,虚拟内存页的大小为4K,为了提高TLB的命中率,大页表的概念被提出来了。

通过使用大页表,TLB中的每一个entry覆盖了更多的内存范围,从而降低了TLB miss的发生。

 

大页表的使用

Linux中大页表的使用不是透明的,必须通过编程人员显式来进行使用。

Linux中大页表的支持是在内核编译的时候,通过CONFIG_HUGETLB_PAGE 和 CONFIG_HUGETLBFS进行制定的。

通过查看

/proc/meminfo文件的内容可以确认自己机器是否启用了大页表功能

#cat /proc/meminfo | grep Huge
HugePages_Total:     0
HugePages_Free:      0
HugePages_Rsvd:      0
Hugepagesize:     4096 kB
我们必须告诉内核大页表的数目,以便供编程人员使用
#echo 4 > /proc/sys/vm/nr_hugepages
设置完成以后,我们可以查看:
#cat /proc/meminfo | grep Huge
HugePages_Total:     4
HugePages_Free:      4
HugePages_Rsvd:      0
Hugepagesize:     4096 kB
 
 
使用实例
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>

#define MB_1 (1024*1024)
#define MB_8 (8*MB_1)

char  *a;
int shmid1;

void init_hugetlb_seg()
{
  shmid1 = shmget(2, MB_8, SHM_HUGETLB
         | IPC_CREAT | SHM_R
         | SHM_W);
  if ( shmid1 < 0 ) {
    perror("shmget");
    exit(1);
  }
  printf("HugeTLB shmid: 0x%x\n", shmid1);
  a = shmat(shmid1, 0, 0);
  if (a == (char *)-1) {
    perror("Shared memory attach failure");
    shmctl(shmid1, IPC_RMID, NULL);
    exit(2);
  }
}

void wr_to_array()
{
  int i;
  for( i=0 ; i<MB_8 ; i++) {
    a[i] = 'A';
  }
}

void rd_from_array()
{
  int i, count = 0;
  for( i=0 ; i<MB_8 ; i++)
    if (a[i] == 'A') count++;
  if (count==i)
    printf("HugeTLB read success :-)\n");
  else
    printf("HugeTLB read failed :-(\n");
}

int main(int argc, char *argv[])
{
  init_hugetlb_seg();
  printf("HugeTLB memory segment initialized !\n");
  printf("Press any key to write to memory area\n");
  getchar();
  wr_to_array();
  printf("Press any key to rd from memory area\n");
  getchar();
  rd_from_array();
  shmctl(shmid1, IPC_RMID, NULL);
  return 0;
}

该例子通过共享内存的方式来使用大页表,通过额外的选项来控制共享内存使用大页表

编译并运行该程序,在程序运行的不同阶段,我们通过查看/proc/meminfo文件的内容来观察系统中大页表的使用情况。

大页表的内部实现

Linux内核中,大页表的实现分为两部分。第一部分是大页表池的创建,该池维护了应用程序可用的大页表,它所占用的内存是通过内核的内存分配函数获取一段连续的内存来实现的。

第二部分则是Linux内核对该页表池的管理,以及如何应对应用程序对大页表的请求。

第一部分,大页表池的创建

Linux的内核文件mm/hugetlb.c中,有 "hugetlb_init" 函数。该函数通过分配连续的常规内存页来实现大页表。

上边提到的函数分配好内存之后,该内存便放入"hugepage_freelists"列表当中。

为了使用一段共享内存,我们必须去创建它。这个动作是通过'shmget' 系统调用来完成的。该系统调用将会调用内核函数 'sys_shmget', 进一步会调用'newseg'。在newseg中,首先会检查用户是否发起了大页表共享内存的请求,如果用户制定了SHM_HUGETLB标志,相关的操作将被指定为'hugetlbfs_file_operations'.

第二部分,大页表池的管理,以及应用程序如何获取所需的大页表

当一个页中断发生时,与该地址对应的“vma”被发现,对应大页表的vma,其中

'vma-> vm_flags'的标志为'VM_HUGETLB',并通过'is_vm_hugetlb_page'检测到. 然后调用'hugetlb_fault'函数。

该函数在页表中创建大页表标志并基于copy-on-write方法从大页表池中分配大页表

 
 
 

Logo

更多推荐