【问题标题】:Multiple read a single write causes a deadlock in Linux character driver?多读单写导致Linux字符驱动死锁?
【发布时间】:2015-04-22 13:46:14
【问题描述】:

我正在开发一个基于字符设备驱动程序的示例 Linux 模块(使用 misc 驱动程序以方便使用),我发现了一些不寻常的行为。

我有文件操作的打开、读取、写入和释放功能,我的杂项设备被命名为test_device

我的设备有一个环形缓冲区,可以存储来自写入系统调用的数据,而读取系统调用可以读取数据。如果缓冲区已满,则调用 write 系统调用的进程将休眠,如果缓冲区为空,则调用 read 系统调用的进程将休眠。

有一个信号量(计数为 1,如互斥锁)以避免多次读取,从而避免出现竞争条件。这对于单个读取和单个写入过程来说效果很好,没有任何问题

现在我使用命令将设备作为 2 个读取实例打开

cat /dev/test_device 

正如预期的那样,第二个 cat 进程休眠了。我只有一个进程要写

ls > /dev/test_device

ls 命令甚至不调用 open 系统调用。谁能解释一下 open 系统调用失败的原因。

代码如下

static int hr_open(struct inode *inode, struct file *file)
{
    pr_info("process %i (%s) enters open\n", current->pid, current->comm);
    if (file->f_mode & FMODE_READ) down_interruptible(&rd_sem);

    pr_info("process %i (%s) leaves open\n", current->pid, current->comm);

    return 0;
}

static int hr_release(struct inode *inode, struct file *file)
{
    pr_info("process %i (%s) enters release\n", current->pid, current->comm);
    if (file->f_mode & FMODE_READ) up(&rd_sem);

    pr_info("process %i (%s) leaves release\n", current->pid, current->comm);

    return 0;
}

static ssize_t hr_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
    pr_info("process %i (%s) enters read\n", current->pid, current->comm);

    if (wait_event_interruptible(wq_rd, head != tail))
        return -ERESTARTSYS;    

    if (put_user(buff[head], buffer)) 
        return -EFAULT;

    head = (head + 1) % BUFF_SIZE;

    wake_up_interruptible(&wq_wr);
    pr_info("process %i (%s) leaves read\n", current->pid, current->comm);

    return 1;
}

static ssize_t hr_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
    pr_info("process %i (%s) enters write\n", current->pid, current->comm);

    if (wait_event_interruptible(wq_wr, tail != ((head + 1) % BUFF_SIZE)))
        return -ERESTARTSYS;    

    if (get_user(buff[tail], buffer)) 
        return -EFAULT; 

    tail = (tail + 1) % BUFF_SIZE;

    wake_up_interruptible(&wq_rd);
    pr_info("process %i (%s) leaves write\n", current->pid, current->comm);

    return 1;
}

【问题讨论】:

  • 如果你执行 "ls >/dev/test_device" 你不应该期望 ls 会打开你的设备。外壳会打开它;这就是'>'的作用。如果您确实遇到了开放式故障,那么包含 errno 会很有帮助。
  • 我也使用程序来做到这一点。我有一个程序位于 while 循环中,直到我得到一些数据并且效果相同
  • 创建一个简单的程序...像这样。 main() { int fd = open("/dev/test_device", O_WRONLY);如果 (fd >= 0) 写(fd, "FOO", 3); } 在 strace 下运行它,发现系统调用接口发生了什么。那么这里的人会更好地帮助你。
  • 我创建了一个程序来从设备读取并运行相同的 2 个实例。第一个实例在读取时休眠,并按预期等待数据 17:30:15.967809 open("/dev/test_dev", O_RDONLY) = 3 17:30:15.967840 read(3, 第二个实例在信号量等待获取它 17:30:30.405666 open("/dev/test_dev", O_RDONLY 现在我使用命令 strace -ttT echo test>/dev/test_device 进行写入,并且 strace 没有输出。

标签: linux linux-kernel


【解决方案1】:

我在您的代码中注意到您打开信号量锁并在释放函数中释放它。我通常不建议在一个函数中持有一个锁并在另一个函数中释放它,因为这可能会导致锁定问题或死锁。

    static int hr_open(struct inode *inode, struct file *file)
    {
    ...    
        if (file->f_mode & FMODE_READ) down_interruptible(&rd_sem);
    ...
    }

    static int hr_release(struct inode *inode, struct file *file)
    {
     ...

       if (file->f_mode & FMODE_READ) up(&rd_sem);

     ...
    }

我的建议是在读写函数中移动信号量锁(向下和向上)。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2022-11-11
  • 1970-01-01
  • 1970-01-01
  • 2018-04-20
  • 2012-09-20
  • 2018-06-04
  • 2016-12-22
  • 1970-01-01
相关资源
最近更新 更多