无法从linux内核获取复制用户工作

无法从linux内核获取复制用户工作,linux,linux-kernel,kernel,driver,linux-device-driver,Linux,Linux Kernel,Kernel,Driver,Linux Device Driver,我们有一个内核驱动程序和一个与此驱动程序交互的用户空间应用程序。用户空间应用程序使用posix_memalign分配内存块,并将这些地址传递给内核驱动程序,如下所示: struct dma_cmd { int cmd; int usr_buf_size; char *buf; }; struct set_mem_addr_cmd_struct { int channel_id; char *ad

我们有一个内核驱动程序和一个与此驱动程序交互的用户空间应用程序。用户空间应用程序使用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;
    }
。。。然后在内核中,我们从包含目标用户空间地址的用户空间读取消息:

...
...
    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);
    }
。。。然后驱动程序生成一个内核线程,该线程进入循环并尝试将示例数据复制到用户空间:

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);
}

有没有办法弄清楚为什么copy\u to\u用户调用不起作用?

基本上不起作用。因为很多上下文都会被切换,所以在您将复制到用户之前。。。因此,您无法猜测将复制哪个进程地址空间。

生成的内核线程根本无法访问用户空间内存

虽然内核的内存在所有进程(线程)之间共享,并切换到内核代码,用户空间内存是每个进程的,只能从拥有该内存的进程访问。换句话说,地址空间的概念即使在进程执行内核代码时也适用

注意,
access\u ok
check是粗略的,它只是告诉我们内存不是内核的。例如,见本节


如果您想在内核空间和用户进程之间共享内存区域,您可以在debugfs中创建(在驱动程序中)文件或作为字符设备,并实现其
mmap
功能。

是进程与
驱动程序主代码
(其中
while(1)
循环运行)不同,哪个执行ioctl命令
CMD\u SET\u MEM\u ADDR
?请注意,只有执行了用户代码的进程才能访问用户空间数据。@Tsyvarev:我不确定是否理解您的评论。让我试着澄清一下:while(1)在内核驱动程序中的内核线程上运行。CMD_SET_MEM_ADDR命令是从用户代码(无线程)发送的,CMD_SET_MEM_ADDR命令是由内核驱动程序接收的。基本上,kthread将继承前面的一个mm(),因此用户部分始终处于活动状态。这里的问题是,mm不是作者所需要的。@AlexHoppus,mm的继承对于分叉进程是正确的。纯内核线程(在内核内部创建)根本无法访问用户部分。链接中的第一个答案也是这样的。@Tsyvarev:所以如果我不是从内核线程内部而是从内核驱动代码的其他地方复制用户,那么它应该可以工作?不,它不会工作。模拟:运行一个用户空间程序,打印指向其
a
变量的指针,然后将此指针传递给另一个用户空间程序,并期望它在第一个程序中使用
a
变量。因为两个程序有不同的地址空间,所以第二个程序不能直接(通过指针)访问第一个程序的数据。@Tsyvarev仔细阅读,您就会得到它如果为空,则进程没有进程地址空间,换句话说,这是一个内核线程。但您也知道,即使内核线程不能访问用户空间内存,它仍然需要访问内核空间。因为每个进程的内核空间都是100%相同的,所以内核线程可以自由使用以前运行的进程拥有的内存描述符(mm)“请参阅mm和active_mm,这也可能会有所帮助
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);
}
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