Linux中保留内存(Reserved memory)是指把系统中的一部分内存保留起来,内核不会为它建立页表,一般的应用程序无法访问到这段内存。在板卡调试、内存测试和设备DAM调试的过程中,可以运用这种办法,先验证系统在只有低端内存的情况下能否顺利启动;此外,服务器和存储系统的环境下,也可以用这种方法从大量系统内存中保留出一部分,留给特殊用途使用或者模拟诸如NVDIMM等设备。因此,有必要对Reserved Memory的使用有一定了解。
1.实现保留内存
Linux内核启动参数cmdline提供了大量的选项,用来设置内核启动的参数和配置。其中“mem”选项就是用来限制内核可以看到的系统内存的大小,因此通过设置
”mem”参数就能实现保留内存。当”mem”参数指定的size小于系统实际可用的物理内存大小时,实际系统内存中出去mem指定的剩下部分就是reserved memory。例如如果某个系统有64G物理内存,通过添加mem=48G到kernel command line,就能保留最末尾的(64-48)=16G高端内存。
观察和对比有无mem参数kernel启动后的/proc/iomem、dmesg的输出,可以看到64G的物理内存映射到CPU地址是0x100000000~0xfffffffff,而48G的高端物理内存映射的CPU地址空间地址是:0x100000000~0xbffffffff,因此加上mem=48G后,CPU物理地址0xc00000000~0xFFFFFFFFF对应的从48G到64G的内存就给保留了出来。
2.使用保留内存
由于内核启动的时候都没有为保留内存建立页表,无论是内核提供的kmalloc()还是dma_malloc_coherence()函数,抑或是应用程序的malloc(),realloc()等函数都无法从保留内存申请空间。所以为了访问保留内存,需要使用某种方法建立起user space address到保留内存物理地址的映射。有两中方法可以实现这种映射:
一、基于某些驱动提供的接口的实现
许多设备驱动能把自身特有的物理地址空间映射到内核虚拟地址空间,一些SDK软件包还提供了基于设备驱动的API接口,来实现用户态地址和设备特有物理地址空间的映射。为此,可以参考借鉴这部分代码实现对保留内存的映射。
这种方法的好处是这种映射方式自身能够实现cache/uncache/write combing/writ thourgh等不同级别的cache一致性,它的映射方式和所参考的设备驱动里指定的属性一样。当然,缺点也显而易见,它使得本来和其他设备没有任何关系和瓜葛的reserve memory,使用的时候变得依赖于某种设备驱动以及对应的SDK库文件。
二、基于/dev/mem的实现
众所周知,Linux内核、驱动的实现时充分考虑了机制和规则相分离的原则。/dev/mem就是内核提供给用户把一段物理内存地址映射到用户态空间的机制,该机制主要依赖于mmap()系统调用。该系统调用为从指定的起始地址开始、指定长度的一段内存建立起页表,并且映射到当前程序的空闲的用户态地址空间。
具体的实现可以参考下面的示例代码:
char *vmem = NULL;
//FILE * fp= fopen("/dev/mem", "w+");
int fp = open("/dev/mem", O_RDWR | O_SYNC);
if (fp < 0) {
printf("Open /dev/mem error!\n");
return -1;
}
vmem = mmap(NULL, LEN, PROT_READ | PROT_WRITE, MAP_SHARED, fp,
RESERVERD_MEM_START);
if (vmem == NULL) {
printf("mmap reserver mem on /dev/mem error!\n");
return -1;
}
这种方法的好处是依赖少,只在user space开发,调试方便,不足指出是较之上面的方法倚赖于/dev/mem驱动的实现,不能保证映射的内存是uncached,而且调用层次深些。
通过以上的分析和比较不难看到,实际开发中最终使用哪一种,需要根据实际的项目要求和资源来选择。