在 Linux 开发过程中,申请内存后,某些时候需要用物理地址传给其他外设进行写入或者读取操作,同时考虑到防止被操作系统 sawp,导致实际的物理地址发生变化,从而在操作对应的虚拟地址时无法正常运行等。
物理地址的获取
获取对应的物理地址代码为:
int mem_addr_vir2phy(unsigned long vir, unsigned long *phy)
{
int fd;
int page_size=getpagesize(); //系统设定的页面大小
unsigned long vir_page_idx = vir/page_size; //虚拟地址相对于0X0经过的页面数
unsigned long pfn_item_offset = vir_page_idx*sizeof(uint64_t); //在pagemap文件中的偏移量
uint64_t pfn_item;
//以只读方式打开pagemap文件
fd = open(page_map_file, O_RDONLY);
if (fd<0)
{
printf("open %s failed", page_map_file);
return -1;
}
//将游标移到对应项的起始位置
if ((off_t)-1 == lseek(fd, pfn_item_offset, SEEK_SET))
{
printf("lseek %s failed", page_map_file);
return -1;
}
//读取对应项的值并判断读取位数
if (sizeof(uint64_t) != read(fd, &pfn_item, sizeof(uint64_t)))
{
printf("read %s failed", page_map_file);
return -1;
}
//判断物理页是否在内存上
if (0==(pfn_item & PFN_PRESENT_FLAG))
{
printf("page is not present");
return -1;
}
//如果在内存上,物理页号加上偏移地址就是物理地址
//对应项bit0-54位表示物理页号
*phy = (pfn_item & PFN_MASK)*page_size + vir % page_size;
return 0;
}
内存锁住
锁住内存是为了防止这段内存被操作系统 swap 掉。并且由于此操作风险高,仅超级用户可以执行。
#include <sys/mman.h>
int mlock(const void *addr, size_t len); // 锁住指定内存块
int munlock(const void *addr, size_t len); // 解锁指定内存块
int mlockall(int flags); // 锁住全部
int munlockall(void); // 解锁全部
系统调用 mlock 家族允许程序在物理内存上锁住它的部分或全部地址空间。这将阻止Linux 将这个内存页调度到交换空间(swap space),即使该程序已有一段时间没有访问这段空间。