2017-12-18 187 views
0

我为我的内核模块实现了一个char设备并为其实现了一个读取函数。读函数调用copy_to_user将数据返回给调用者。我最初以阻塞的方式实现了读取功能(使用wait_event_interruptible),但即使以非阻塞方式实现读取,问题也会再现。我的代码在MIPS处理器上运行。copy_to_user在char设备读取函数中返回一个错误

用户空间程序打开char设备并读入堆栈中分配的缓冲区。

我发现的是偶尔copy_to_user将无法​​复制任何字节。此外,即使我用(仅用于检查......我知道这不是正确的做法)调用替换copy_to_user,然后立即打印目标缓冲区,我看到memcpy失败复制任何字节。

我真的不知道如何进一步调试 - 我如何确定为什么内存不被复制?流程上下文有可能是错误的吗?

编辑:下面是一些伪代码概述了代码目前的样子:

用户模式(重复运行):

char buf[BUF_LEN]; 
FILE *f = fopen(char_device_file, "rb"); 
fread(buf, 1, BUF_LEN, f); 
fclose(f); 

内核模式:

char_device = 
    create_char_device(char_device_name, 
     NULL, 
     read_func, 
     NULL, 
     NULL); 

int read_func(char *output_buffer, int output_buffer_length, loff_t *offset) 
{ 
    int rc; 
    if (*offset == 0) 
    { 
     spin_lock_irqsave(&lock, flags); 

     while (get_available_bytes_to_read() == 0) 
     { 
      spin_unlock_irqrestore(&lock, flags); 
      if (wait_event_interruptible(self->wait_queue, get_available_bytes_to_read() != 0)) 
      { 
       // Got a signal; retry the read 
       return -ERESTARTSYS; 
      } 

      spin_lock_irqsave(&lock, flags); 
     } 

     rc = copy_to_user(output_buffer, internal_buffer, bytes_to_copy); 

     spin_unlock_irqrestore(&lock, flags); 
    } 
    else rc = 0; 

    return rc; 
} 
+1

你的代码有问题,但你不显示它。 – Tsyvarev

+0

@Tsyvarev添加了一些伪代码,希望能够说明代码所做的工作。不幸的是,我还没有能够缩小到一个小的,再现的例子。 – YSK

+0

@YSK你已经声明了两次'int rc'。从'int rc = copy_to_user(output_buffer,internal_buffer,bytes_to_copy)''行中删除'int'。 – Gaurav

回答

0

它进行了相当多的调试,但最终Tsyvarev的提示(关于不采用自旋锁调用copy_to_user的评论)似乎是原因。

我们的程序有一个后台线程,偶尔会启动一个新进程(fork + exec)。当我们禁用此线程时,一切运行良好。我们拥有的最好理论是,fork使我们所有的内存页面都可以在写入时进行复制,所以当我们试图复制它们时,内核必须做一些无法使用自旋锁执行的工作。希望它至少有一定意义(尽管我已经猜到这只适用于子进程,并且父进程页面仍然是可写的,但是谁知道......)。

我们重写了我们的代码,使其无法锁定,问题消失。

现在我们只需要验证我们的无锁代码在不同的架构上确实是安全的。易如反掌。