正确的Linux内核等待队列和列表之间的交互

时间:2014-05-06 01:49:17

标签: multithreading linux-kernel

我正在编写一个Linux内核模块,该模块涉及从不同进程上下文中读取/写入的列表,并且感觉我缺少与pthread_cond_wait()和co相当的功能。来自用户空间。

天真地我可能会写这样的东西:

static LIST_HEAD(request_list);
static DEFINE_MUTEX(request_list_mutex);
static DECLARE_WAIT_QUEUE_HEAD(request_list_post_wq);

static void post_request(request_t *request)
{
    mutex_lock(request_list_mutex);
    list_add(request, request_list);
    mutex_unlock(request_list_mutex);
    wake_event(request_list_post_wq);
}

static void wait_and_consume_request()
{
    mutex_lock(request_list_mutex);
    if(list_empty(request_list)) {
        mutex_unlock(request_list_mutex);
        wait_event(request_list_post_wq, !list_empty(request_list));
        mutex_lock(request_list_mutex);
    }

    // do something with request

    mutex_unlock(request_list_mutex);
}

然而,这看起来在消费者功能中将在非空列表中唤醒然后在有多个消费者时重新获取互斥锁之间存在竞争条件。与此同时,我必须在等待之前释放互斥锁,否则将无法添加到列表中。

我考虑编写一个锁定request_list的函数,只有当它仍然为空时才解锁它并将其用作wait_event的条件...但谷歌搜索我已经看过很多人的例子写wait_event(....,!list_empty(...))所以我一定错过了什么?

1 个答案:

答案 0 :(得分:4)

其他人建议的辅助功能根本不需要:

static int list_is_not_empty()
{
    int rv = 1;
    mutex_lock(request_list_mutex);
    rv = !list_empty(request_list);
    mutex_unlock(request_list_mutex);
    return rv;
}

没有必要锁定列表只是为了看它是否为空。简单地说:

static void wait_and_consume_request()
{
    wait_event(request_list_post_wq, !list_empty(request_list));
    mutex_lock(request_list_mutex);
    if(!list_empty(request_list)) {
        // do something with request
    }
    mutex_unlock(request_list_mutex);
}

但这并不能保证你实际上是在消费请求。如果我们确实希望确保我们只使用一个请求,那么:

static void wait_and_consume_request()
{
    mutex_lock(request_list_mutex);            
    while(list_empty(request_list)) {
        mutex_unlock(request_list_mutex);
        wait_event(request_list_post_wq, !list_empty());
        lock_mutex();
     }
     // do something with request
     mutex_unlock(request_list_mutex);
}

这是来自drivers / misc / carma / carma-fpga.c中内核的一个真实示例(我刚拿了第一个我能看到的例子)

      spin_lock_irq(&priv->lock);

      /* Block until there is at least one buffer on the used list */
      while (list_empty(used)) {
              spin_unlock_irq(&priv->lock);

              if (filp->f_flags & O_NONBLOCK)
                      return -EAGAIN;

              ret = wait_event_interruptible(priv->wait, !list_empty(used));
              if (ret)
                      return ret;

              spin_lock_irq(&priv->lock);
      }

      /* Grab the first buffer off of the used list */
      dbuf = list_first_entry(used, struct data_buf, entry);
      list_del_init(&dbuf->entry);

      spin_unlock_irq(&priv->lock);