无法从 linux 内核获取 copy_to_user 工作
Can not get copy_to_user work from the linux kernel
我们有一个内核驱动程序和一个与该驱动程序交互的用户 space 应用程序。用户 space 应用程序使用 posix_memalign 分配内存块,并将这些地址传递给内核驱动程序,如下所示:
struct dma_cmd
{
int cmd;
int usr_buf_size;
char *buf;
};
struct set_mem_addr_cmd_struct
{
int channel_id;
char *address;
};
char* register_mem_for_channel(ssize_t f, int channelid) {
struct dma_cmd cmd;
char *buf = (char *)malloc(sizeof(struct set_mem_addr_cmd_struct));
cmd.usr_buf_size = sizeof(struct set_mem_addr_cmd_struct);
cmd.cmd = CMD_SET_MEM_ADDR;
(*(struct set_mem_addr_cmd_struct*)buf).channel_id = channelid;
if ((channelid < 4))
char *addr;
posix_memalign(&addr, 4*1024, 4*1024*8);
if (addr == 0) {
printf("ERROR: Can not allocate aligned frame memory\n");
exit(-1);
}
(*(struct set_mem_addr_cmd_struct*)buf).address = addr;
}
cmd.buf = buf;
write(f, &cmd, 0); // Send to driver
char *addr = (*(struct set_mem_addr_cmd_struct*)buf).address;
printf ("SET memory address for channel %d to %p\n", channelid, addr);
return addr;
}
内核驱动有以下声明:
#define MAX_CHANNELS 8
char *user_addr[MAX_CHANNELS];
...然后在内核中,我们读取来自用户space的消息,其中包含目标用户space地址:
...
...
case CMD_SET_MEM_ADDR:
{
int cid;
if (copy_from_user (&cmd_buf, kcmd.buf, sizeof (struct set_mem_addr_cmd_struct)))
{
printk(KERN_DEBUG "KERN: Set Memory Address FAULT\n");
rc = -EFAULT;
return rc;
}
cid = cmd_buf.channel_id;
user_addr[cid] = cmd_buf.address;
printk(KERN_DEBUG "Set Channel %d Addr : %p\n", cmd_buf.channel_id, cmd_buf.address);
}
... 然后驱动程序生成一个内核线程,该线程进入循环并尝试将示例数据复制给用户 space:
while (1) {
msleep(333);
for (i=0; i<64; i++) imagedata[i] = i;
printk(KERN_DEBUG "copy to user buffer A at address %p", user_addr[0]);
if (!access_ok(VERIFY_WRITE, user_addr[0], 64)) {
printk(KERN_DEBUG "ERROR: Can not access userspace addr\n");
}
errcode = copy_to_user(user_addr[0], &imagedata[0], 64);
if (errcode != 0) printk(KERN_DEBUG "ERROR: COPY TO USER A FAILED!!! (errcode:%ld)\n", errcode);
}
在上面的代码中,access_ok总是returns为真,所以我们不会得到访问错误。然而,copy_to_user 总是调用 returns 64,表明没有字节被复制到用户 space.
在用户 space 中,我们有打印出字节的代码,我们可以确认没有任何内容写入目标。以下是内核和用户端的日志:
Kernel:
[ 9594.668322] Set Channel 0 Addr : 00007f460d68b000
[ 9594.719297] copy to user buffer A at address 00007f460d68b000
[ 9594.719341] COPY TO USER A FAILED!!! (errcode:64)
User:
USER: SET memory address for channel 0 to 0x7f460d68b000
USER: SIGNAL RECEIVED: value 1
USER: Callback for data received
USER: FIRST 10 BYTES for addr 0x7f460d68b000: 0 0 0 0 0 0 0 0 0 0
有没有办法弄清楚为什么 copy_to_user 调用不起作用?
基本上那是行不通的。因为很多上下文会被切换,在你制作copy_to_user之前......所以你无法猜测你将复制哪个进程地址space。
您生成的内核线程根本无法访问用户-space 内存。
虽然内核的内存在所有进程(线程)之间共享,切换到内核代码,user-space内存是每个进程,并且可以访问仅从进程中,拥有此内存。换句话说,即使进程执行内核代码,地址space概念也适用。
请注意,access_ok
检查很粗略,它只是说明内存不是内核的。参见,例如,这个 .
如果你想在内核 space 和用户进程之间共享内存区域,你可以在 debugfs 中创建(在驱动程序中)文件或作为字符设备并实现其 mmap
功能。
我们有一个内核驱动程序和一个与该驱动程序交互的用户 space 应用程序。用户 space 应用程序使用 posix_memalign 分配内存块,并将这些地址传递给内核驱动程序,如下所示:
struct dma_cmd
{
int cmd;
int usr_buf_size;
char *buf;
};
struct set_mem_addr_cmd_struct
{
int channel_id;
char *address;
};
char* register_mem_for_channel(ssize_t f, int channelid) {
struct dma_cmd cmd;
char *buf = (char *)malloc(sizeof(struct set_mem_addr_cmd_struct));
cmd.usr_buf_size = sizeof(struct set_mem_addr_cmd_struct);
cmd.cmd = CMD_SET_MEM_ADDR;
(*(struct set_mem_addr_cmd_struct*)buf).channel_id = channelid;
if ((channelid < 4))
char *addr;
posix_memalign(&addr, 4*1024, 4*1024*8);
if (addr == 0) {
printf("ERROR: Can not allocate aligned frame memory\n");
exit(-1);
}
(*(struct set_mem_addr_cmd_struct*)buf).address = addr;
}
cmd.buf = buf;
write(f, &cmd, 0); // Send to driver
char *addr = (*(struct set_mem_addr_cmd_struct*)buf).address;
printf ("SET memory address for channel %d to %p\n", channelid, addr);
return addr;
}
内核驱动有以下声明:
#define MAX_CHANNELS 8
char *user_addr[MAX_CHANNELS];
...然后在内核中,我们读取来自用户space的消息,其中包含目标用户space地址:
...
...
case CMD_SET_MEM_ADDR:
{
int cid;
if (copy_from_user (&cmd_buf, kcmd.buf, sizeof (struct set_mem_addr_cmd_struct)))
{
printk(KERN_DEBUG "KERN: Set Memory Address FAULT\n");
rc = -EFAULT;
return rc;
}
cid = cmd_buf.channel_id;
user_addr[cid] = cmd_buf.address;
printk(KERN_DEBUG "Set Channel %d Addr : %p\n", cmd_buf.channel_id, cmd_buf.address);
}
... 然后驱动程序生成一个内核线程,该线程进入循环并尝试将示例数据复制给用户 space:
while (1) {
msleep(333);
for (i=0; i<64; i++) imagedata[i] = i;
printk(KERN_DEBUG "copy to user buffer A at address %p", user_addr[0]);
if (!access_ok(VERIFY_WRITE, user_addr[0], 64)) {
printk(KERN_DEBUG "ERROR: Can not access userspace addr\n");
}
errcode = copy_to_user(user_addr[0], &imagedata[0], 64);
if (errcode != 0) printk(KERN_DEBUG "ERROR: COPY TO USER A FAILED!!! (errcode:%ld)\n", errcode);
}
在上面的代码中,access_ok总是returns为真,所以我们不会得到访问错误。然而,copy_to_user 总是调用 returns 64,表明没有字节被复制到用户 space.
在用户 space 中,我们有打印出字节的代码,我们可以确认没有任何内容写入目标。以下是内核和用户端的日志:
Kernel:
[ 9594.668322] Set Channel 0 Addr : 00007f460d68b000
[ 9594.719297] copy to user buffer A at address 00007f460d68b000
[ 9594.719341] COPY TO USER A FAILED!!! (errcode:64)
User:
USER: SET memory address for channel 0 to 0x7f460d68b000
USER: SIGNAL RECEIVED: value 1
USER: Callback for data received
USER: FIRST 10 BYTES for addr 0x7f460d68b000: 0 0 0 0 0 0 0 0 0 0
有没有办法弄清楚为什么 copy_to_user 调用不起作用?
基本上那是行不通的。因为很多上下文会被切换,在你制作copy_to_user之前......所以你无法猜测你将复制哪个进程地址space。
您生成的内核线程根本无法访问用户-space 内存。
虽然内核的内存在所有进程(线程)之间共享,切换到内核代码,user-space内存是每个进程,并且可以访问仅从进程中,拥有此内存。换句话说,即使进程执行内核代码,地址space概念也适用。
请注意,access_ok
检查很粗略,它只是说明内存不是内核的。参见,例如,这个
如果你想在内核 space 和用户进程之间共享内存区域,你可以在 debugfs 中创建(在驱动程序中)文件或作为字符设备并实现其 mmap
功能。