我知道copy_to_user
/ copy_from_user
, get_user
/put_user
函数就是为此目的。
我的问题是,给定用户空间地址/指针,我一般如何访问内核地址指向的数据?
我可以想象,首先我必须确保包含页面应该在物理内存中(而不是在磁盘中)。
你下一步怎么做?我可以使用指向某些用户空间数据的指针*p
在哪里直接引用数据吗?p
还是我必须先调用kmap
以将包含的物理页框映射到内核虚拟地址空间?为什么?
我知道copy_to_user
/ copy_from_user
, get_user
/put_user
函数就是为此目的。
我的问题是,给定用户空间地址/指针,我一般如何访问内核地址指向的数据?
我可以想象,首先我必须确保包含页面应该在物理内存中(而不是在磁盘中)。
你下一步怎么做?我可以使用指向某些用户空间数据的指针*p
在哪里直接引用数据吗?p
还是我必须先调用kmap
以将包含的物理页框映射到内核虚拟地址空间?为什么?
您可能会发现这很有用。
让我们重复一遍,read 和 write 方法的 buff 参数是一个用户空间指针。因此,它不能被内核代码直接取消引用。这种限制有几个原因:
根据您的驱动程序运行的架构以及内核的配置方式,用户空间指针在内核模式下运行时可能根本无效。该地址可能没有映射,或者它可能指向其他一些随机数据。
即使指针在内核空间中确实表示相同的意思,用户空间内存也是分页的,并且在进行系统调用时,所讨论的内存可能不会驻留在 RAM 中。尝试直接引用用户空间内存可能会产生页面错误,这是内核代码不允许做的事情。结果将是“哎呀”,这将导致进行系统调用的进程死亡。
有问题的指针由用户程序提供,可能是错误的或恶意的。如果您的驱动程序曾经盲目地取消引用用户提供的指针,它提供了一个开放的入口,允许用户空间程序访问或覆盖系统中任何地方的内存。如果您不希望对损害用户系统的安全性负责,则永远不能直接取消引用用户空间指针。
来源: http: //www.makelinux.net/ldd3/chp-3-sect-7
也就是说,我自己很想知道如果用户空间地址确实有效会发生什么,并且上述条件都不适用......
光有指针是不够的!您需要知道该指针“属于”哪个进程。
当进程被抢占时,指针指向另一个进程的地址空间。地址可能不再映射了,yadda yadda,
如果该进程将是您访问数据时的当前进程,那么您应该使用 copy_to_user/copy_from_user 函数。
如果进程可能被调度,您可以尝试 mlock() RAM 中的页面并找出该页面的物理 RAM 地址。每当您想访问它时,您将该物理页面映射到内核虚拟地址。
笔记:
不同的用户空间应用程序有不同的页表。
下面是将用户空间虚拟地址转换为物理地址的示例代码。它适用于 x86 平台。
taskpid = find_get_pid(curpid);
task = pid_task(taskpid, PIDTYPE_PID );
mm = get_task_mm(task);
down_read(&mm->mmap_sem);
start_vaddr = vaddr;
end_vaddr = 0xC0000000;
while( start_vaddr < end_vaddr){
u32 end;
end = (( start_vaddr + PMD_SIZE) & PMD_MASK);
if( end < start_vaddr || end > end_vaddr)
end = end_vaddr;
ret = walk_pgd(start_vaddr, end, mm);
if(ret != 0){
printk("ret: %08x \n", ret);
break;
}
start_vaddr = end;
}
up_read(&mm->mmap_sem);
paddr = ret;
kaddr = __va(paddr);
mmput(mm);
您需要follow
一个地址来获取相应的page
结构(请参阅follow_page的示例)。接下来,获取page
结构体,您需要通过kmap
or将其映射到内核的地址空间kmap_atomic
。