Linux 中通过虚拟地址获取物理地址并锁定

在 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),即使该程序已有一段时间没有访问这段空间。

posted @ 2022-06-10 19:04  大橙子疯  阅读(518)  评论(0编辑  收藏  举报