【问题标题】:linux wake_up_interruptible() having no effectlinux wake_up_interruptible() 没有效果
【发布时间】:2015-06-28 03:19:07
【问题描述】:

我正在为操作系统类编写一个“休眠”设备驱动程序。

它的工作方式是,用户通过read()/write()访问设备。 当用户像这样向设备写入:write(fd, &wait, size) 时,设备将在wait 值的秒数内进入睡眠状态。如果等待时间到期,则驱动程序的 write 方法返回 0 并且程序结束。但是,如果用户在进程在等待队列中休眠时从驱动程序读取数据,则驱动程序的 write 方法会立即返回休眠进程在超时之前等待的秒数。

另一个问题是创建了 10 个设备实例,这 10 个设备中的每一个都必须相互独立。因此,对设备 1 的读取只能唤醒设备 1 上的休眠进程。

提供了很多代码,我主要负责编写驱动程序的read()write()方法。

我尝试解决保持设备相互独立问题的方法是包含两个大小为 10 的全局静态数组。一个是 wait_head_queue_t 类型,另一个是 Int(布尔标志)类型。当我通过open() 打开设备时,这两个数组都会初始化一次。问题是当我调用wake_up_interruptible() 时,什么也没有发生,程序在超时时终止。这是我的写方法:

ssize_t sleepy_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos){
struct sleepy_dev *dev = (struct sleepy_dev *)filp->private_data;

ssize_t retval = 0;
int mem_to_be_copied = 0;

if (mutex_lock_killable(&dev->sleepy_mutex))  
{ 
    return -EINTR; 
}

// check size
if(count != 4)  // user must provide 4 byte Int
{
    return EINVAL; // = 22
}
//  else if the user provided valid sized input...
else
{
      if((mem_to_be_copied = copy_from_user(&long_buff[0], buf, count))) 
      { 
        return -EFAULT; 
      }

      // check for negative wait time entered by user
      if(long_buff[0] > -1)// "long_buff[]"is global,for now only holds 1 value
      {
        proc_read_flags[MINOR(dev->cdev.dev)] = 0;  //****** flag array
        retval = wait_event_interruptible_timeout(wqs[MINOR(dev->cdev.dev)],   proc_read_flags[MINOR(dev->cdev.dev)] == 1, long_buff[0] * HZ) / HZ;
        proc_read_flags[MINOR(dev->cdev.dev)] = 0; // MINOR numbers for each
                                    // device correspond to array indices 
                                       // devices 0 - 9
                                         // "wqs" is array of wait queues
      }
      else
      {
         printk(KERN_INFO "user entered negative value for sleep time\n");
      }
}

mutex_unlock(&dev->sleepy_mutex);
return retval;}

与该主题的许多示例不同,我在调用wait_event_interruptible_timeout() 之前立即将标志切换回零,因为标志值似乎在程序的后续运行之间徘徊。这是我的读取方法的代码:

 ssize_t sleepy_read(struct file *filp, char __user *buf, size_t count, 
        loff_t *f_pos){

 struct sleepy_dev *dev = (struct sleepy_dev *)filp->private_data;
 ssize_t retval = 0;

 if (mutex_lock_killable(&dev->sleepy_mutex))
     return -EINTR;

 // switch the flag
 proc_read_flags[MINOR(dev->cdev.dev)] = 1; // again device minor numbers
                                             // correspond to array indices 

 // TODO:   this is not waking up the process in write!
 // wake up the queue
 wake_up_interruptible(&wqs[MINOR(dev->cdev.dev)]);

 mutex_unlock(&dev->sleepy_mutex);

return retval;}

我尝试测试程序的方式是拥有两个 main.c,一个用于写入设备,一个用于从设备读取,我只是 ./a.out 在我的虚拟 ubuntu 安装中将它们放在单独的控制台中盒子。另一件事,它现在的设置方式,写入或读取a.outs 在超时发生之前都不会返回。我为代码的参差不齐的格式道歉。我不确定这里到底发生了什么,所以任何帮助将不胜感激!谢谢!

【问题讨论】:

  • 您是否确认 wake_up_interruptible(&wqs[MINOR(dev->cdev.dev)]) 在达到超时间隔之前正在调用?
  • jjm,你是对的,wake_up_interruptible 直到超时后才会被调用。

标签: linux linux-kernel driver device


【解决方案1】:

您的 write 方法在等待事件时保持 sleepy_mutex。所以 read 方法等待mutex_lock_killable(&dev->sleepy_mutex) 而互斥锁被作者解锁。只有当 writer 超时,并且 write 方法返回时才会发生。这是你观察到的行为。

通常,wait_event* 在任何临界区之外执行。这可以通过使用此类宏的 _lock 后缀变体来实现,或者简单地将此类宏的 cond 参数与自旋锁获取/释放对包装:

int check_cond()
{
    int res;
    spin_lock(&lock);
    res = <cond>;
    spin_unlock(&lock);
    return res;
}
...
    wait_event_interruptible(&wq, check_cond());

不幸的是,wait_event-family 宏无法使用,此时条件检查应使用互斥体进行保护。在这种情况下,您可以使用带有手动条件检查代码的 wait_woken() 函数。或者重写您的代码,而无需围绕条件检查进行互斥锁/解锁。

为了实现“读者唤醒作家,如果它是睡眠”功能,您可以采用来自该答案 https://stackoverflow.com/a/29765695/3440745 的代码。

编写器代码:

    //Declare local variable at the beginning of the function
    int cflag;
    ...
    // Outside of any critical section(after mutex_unlock())
    cflag = proc_read_flags[MINOR(dev->cdev.dev)];
    wait_event_interruptible_timeout(&wqs[MINOR(dev->cdev.dev)],
        proc_read_flags[MINOR(dev->cdev.dev)] != cflag, long_buff[0]*HZ);

阅读器代码:

    // Mutex holding protects this flag's increment from concurrent one.
    proc_read_flags[MINOR(dev->cdev.dev)]++;
    wake_up_interruptible_all(&wqs[MINOR(dev->cdev.dev)]);

【讨论】:

    猜你喜欢
    • 2011-12-04
    • 2015-06-28
    • 2012-10-30
    • 2018-05-21
    • 2018-07-28
    • 2018-05-15
    • 1970-01-01
    • 2011-10-01
    • 2015-05-31
    相关资源
    最近更新 更多