2014-05-06 32 views
3

我正在編寫一個Linux內核模塊,它涉及從不同的進程上下文中讀取/寫入的列表,並感覺我缺少與pthread_cond_wait()和co。來自用戶空間。Linux內核等待隊列和列表之間的正確交互

天真我可能會寫這樣的事:

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(...))所以我必須錯過什麼?

回答

3

的輔助函數,其他人認爲是沒有必要的:

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

下面是在司機的/ 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);