无法从 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 功能。