当前位置: 首页 > news >正文

Linux 内存 get_user_pages_remote 函数

文章目录

  • 一、函数简介
  • 二、demo

一、函数简介

// linux/v6.14/source/mm/gup.clong get_user_pages_remote(struct mm_struct *mm,unsigned long start, unsigned long nr_pages,unsigned int gup_flags, struct page **pages,int *locked)
{int local_locked = 1;if (!is_valid_gup_args(pages, locked, &gup_flags,FOLL_TOUCH | FOLL_REMOTE))return -EINVAL;return __get_user_pages_locked(mm, start, nr_pages, pages,locked ? locked : &local_locked,gup_flags);
}
EXPORT_SYMBOL(get_user_pages_remote);

get_user_pages_remote() 的功能是:

在 指定 mm_struct(目标进程地址空间) 中,从给定用户虚拟地址开始,获取(pin)若干页的 struct page *。

这是一个“跨进程”的版本 —— 可用于访问别的进程的用户内存。

get_user_pages_remote 是 Linux 内核中用于获取另一个进程(或当前进程)用户空间地址对应的物理页的函数。它属于 GUP(Get User Pages)机制的一部分,用来在内核态直接访问用户态内存页(例如进行 DMA、零拷贝 I/O、RDMA、KVM 映射等)。

它遍历指定进程(mm)的页表,找到从 start 开始的一系列用户虚拟页,把它们对应的 struct page * 引用计数 +1(防止被回收),并返回这些页面的指针。

使用的一些注意事项:
必须在调用时持有 mmap_lock(读或写锁)。

备注:
内核不推荐使用这个函数,原因如下:
handle_mm_fault() 是内核处理缺页的核心函数。它有个 flag:FAULT_FLAG_ALLOW_RETRY。
如果设置了这个标志,当出现可重试缺页时,内核会:

释放 mmap_lock
处理缺页
重新获取锁
再次重试访问页表

这能避免死锁与卡顿。

但 get_user_pages_remote() 由于接口层次设计问题,无法把这个 flag 传下去,因此它在处理缺页时不能自动重试。结果:

驱动要自己 retry;
性能更低;
在复杂 VMA 状态下容易失败。

推荐使用:get_user_pages_fast()/get_user_pages_unlocked()。

但是get_user_pages_fast()/get_user_pages_unlocked()只能访问本进程的地址空间,不能跨进程访问。

二、demo

接下来给出一个代码示例,编写一个驱动模块访问用户空间的虚拟地址。

用户程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/mman.h>#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PFN_MASK ((1UL << 55) - 1)int main() {int fd;uint64_t va, pa, page, pfn;unsigned long virt_addr;uint64_t file_offset;pid_t pid = getpid();printf("当前进程的 PID: %d\n", pid);// 分配一个页面char *buffer = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);if (buffer == MAP_FAILED) {perror("mmap failed");return 1;}// 写入数据确保页面已分配buffer[0] = 'A';virt_addr = (unsigned long)buffer;printf("虚拟地址: 0x%lx\n", virt_addr);// 打开 pagemap 文件fd = open("/proc/self/pagemap", O_RDONLY);if (fd < 0) {perror("open pagemap failed");munmap(buffer, PAGE_SIZE);return 1;}// 计算在 pagemap 文件中的偏移量file_offset = (virt_addr / PAGE_SIZE) * sizeof(uint64_t);if (lseek(fd, file_offset, SEEK_SET) == (off_t)-1) {perror("lseek failed");close(fd);munmap(buffer, PAGE_SIZE);return 1;}// 读取页表项if (read(fd, &page, sizeof(uint64_t)) != sizeof(uint64_t)) {perror("read pagemap failed");close(fd);munmap(buffer, PAGE_SIZE);return 1;}close(fd);// 检查页面是否在内存中if ((page & (1UL << 63)) == 0) {printf("页面不在内存中\n");munmap(buffer, PAGE_SIZE);return 1;}// 获取物理帧号 (PFN)pfn = page & PFN_MASK;pa = (pfn << PAGE_SHIFT) | (virt_addr & (PAGE_SIZE - 1));printf("物理地址: 0x%lx\n", pa);printf("PFN: 0x%lx\n", pfn);// 阻塞等待,保持页面在内存中printf("进程进入阻塞状态,按回车键退出...\n");getchar();munmap(buffer, PAGE_SIZE);return 0;
}

结果:

$ sudo ./a.out 
当前进程的 PID: 5989
虚拟地址: 0x778653700000
物理地址: 0x18c335000
PFN: 0x18c335
进程进入阻塞状态,按回车键退出...

内核态程序:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched/signal.h>
#include <linux/mm.h>
#include <linux/pid.h>
#include <linux/highmem.h>static int target_pid = -1;
static unsigned long target_vaddr = 0;module_param(target_pid, int, 0444);
MODULE_PARM_DESC(target_pid, "Target process PID");module_param(target_vaddr, ulong, 0444);
MODULE_PARM_DESC(target_vaddr, "Target virtual address (hex)");static int __init vaddr_verify_init(void)
{struct pid *pid_struct;struct task_struct *task;struct mm_struct *mm;struct page *page = NULL;long rc;unsigned long pfn;unsigned long phys_addr;int locked = 1; pr_info("init pid=%d vaddr=0x%lx\n", target_pid, target_vaddr);if (target_pid <= 0 || target_vaddr == 0) {pr_err("invalid parameters\n");return -EINVAL;}pid_struct = find_get_pid(target_pid);if (!pid_struct) {pr_err("find_get_pid failed for %d\n", target_pid);return -ESRCH;}task = pid_task(pid_struct, PIDTYPE_PID);if (!task) {pr_err("pid_task failed\n");put_pid(pid_struct);return -ESRCH;}pr_info("comm = %s\n", task->comm);mm = get_task_mm(task);if (!mm) {pr_err("get_task_mm failed\n");put_pid(pid_struct);return -ESRCH;}mmap_read_lock(mm);//locked 是一个 输入/输出参数 ,输入:调用者是否已持有 mmap_lock//locked = 1 表示我们已持有 mmap_lock//locked 传 NULL 表示你不关心 locked 机制, 让 GUP 自己决定锁的生命周期rc = get_user_pages_remote(mm, target_vaddr & PAGE_MASK, 1, FOLL_GET, &page, &locked);/* 根据 locked 状态决定是否释放锁 */if (locked)mmap_read_unlock(mm);if (rc <= 0) {pr_err("get_user_pages_remote returned %ld\n", rc);mmput(mm);put_pid(pid_struct);return -EFAULT;}/* page pinned; compute PFN and physical address */pfn = page_to_pfn(page);phys_addr = (pfn << PAGE_SHIFT) | (target_vaddr & ~PAGE_MASK);pr_info("success pid=%d vaddr=0x%lx -> pfn=0x%lx phys=0x%lx\n",target_pid, target_vaddr, pfn, phys_addr);put_page(page);mmput(mm);put_pid(pid_struct);return 0;
}static void __exit vaddr_verify_exit(void)
{pr_info("exit\n");
}module_init(vaddr_verify_init);
module_exit(vaddr_verify_exit);MODULE_LICENSE("GPL");

运行结果:

$ make
$ sudo insmod user_virt_to_phy.ko target_pid=5989 target_vaddr=0x778653700000
$ sudo dmesg -c
[ 6545.773724] init pid=5989 vaddr=0x778653700000
[ 6545.773767] comm = a.out
[ 6545.773771] success pid=5989 vaddr=0x778653700000 -> pfn=0x18c335 phys=0x18c335000

get_user_pages_remote的locked也可以传入NULL:

    mmap_read_lock(mm);rc = get_user_pages_remote(mm, target_vaddr & PAGE_MASK, 1, FOLL_GET, &page, NULL);mmap_read_unlock(mm);
http://www.dtcms.com/a/515118.html

相关文章:

  • 【图像处理】图像滤波
  • CSS 列表详解
  • 建设工程规范下载网站商城网站开发的完整流程
  • 同德县网站建设公司海南网站建设及维护
  • 广西送变电建设公司网站深圳市建设工程造价站官网
  • 网站获取访问者qq号码专业的网页设计和网站制作公司
  • 网站建设费账务处理a站下载
  • 哈尔滨网站建设丿薇建立短语
  • 徐州seo网站推广网站开发 页面功能布局
  • 用extjs做的网站wps如何做网站
  • 青羊区建设局网站怎样入驻微信小程序
  • 网站标题几个字合适学生个人网页制作html代码
  • 网站设计规划信息技术教案枣庄三合一网站开发
  • 提升网站流量电子邮件免费注册
  • 广东住房和城乡建设厅官方网站运维工程师累吗
  • 宁波江北区建设局网站如何用wordpress上传根目录
  • 建设实业公司网站设计模板哪家网站做民宿好
  • 网站质量需求页面设计的网站
  • 有经验的南昌网站制作小白 宝塔 wordpress
  • 网站建设引入谷歌地图wordpress淘宝内容
  • 国外网站做网上生意哪个好创可贴网页设计网站
  • 福田附近做网站公司线上销售平台都有哪些
  • app网站开发成本哈尔滨seo优化客户
  • 建设网站的项目策划书网站建设毕业设计模板
  • 微网站自己怎么做的大连旅游网站建设
  • 玉林市建设工程交易中心网站怎样创建自己的公众号
  • 企业网站内使用了哪些网络营销方式线上编程课推荐哪一个
  • 我的世界做神器指令网站wordpress sso插件开发
  • 网站建设应遵循哪几项原则tomcat安装wordpress
  • 手机网站开发语言微分销平台登陆