淺談等待隊列的內部實現(一)

淺談等待隊列的內部實現(二)

http://blog.csdn.net/yyttiao/article/details/7877168


很多時候我們在使用等待隊列的時候,其實並不知道它的內部是怎麼實現的。爲什麼要用add_wait_queue來增加

而後又爲什麼要使用wait_queue_head_t ,sechedule之後怎麼返回到之前的位置,喚醒又做了些什麼事情等等。。
這裏就來簡單的剖析下等待隊列的內部實現原理


其實我在使用等待隊列的時候有這樣的一個問題,並且困擾我很久,先看一段代碼

DECLARE_WAITQUEUE(wait,current);
add_wait_queue(&devp->w_wait,&wait);	
while (devp->current_size == GLOBAMEM_SIZE) {                                   //無數據可讀,進入睡眠
	__set_current_state(TASK_INTERRUPTIBLE);                                //修改當前進程狀態爲淺睡眠
	schedule();                                                             //重新調度進程
	if (signal_pending(current)) {
		ret = -ERESTARTSYS;
		goto out2;
	}	
}

/* *********一般情況下,也是大多數情況下,應該說必須這麼做吧********** */
在其它位置上肯定有會一個喚醒寫上面的等待隊列,否則就沒法執行了。。。
wake_up_interruptible(&devp->w_wait);

之前一致覺得奇怪,爲什麼add_wait_queue之後就可以被喚醒呢。。爲什麼在其它地方調wake_up_interruptible就可以重新讓等待的
進程被調度起來呢。。


其實可能並不是所有的人都跟我一樣,會迷茫。。但是久而久之我就明白了。

首先看一下wait_queue_t

typedef struct __wait_queue wait_queue_t;
struct __wait_queue {
	unsigned int flags;
#define WQ_FLAG_EXCLUSIVE	0x01
	/*  取值爲WQ_FLAG_EXCLUSIVE(=1)表示互斥進程,由內核有選擇的喚醒.爲0時表示非互斥進程,由內核在
         **事件發生時喚醒所有等待進程.
         **/
	void *private;
	wait_queue_func_t func;
	struct list_head task_list;
};

在看一下初始化宏
#define __WAITQUEUE_INITIALIZER(name, tsk) {				\
	.private	= tsk,						\
	.func		= default_wake_function,			\
	.task_list	= { NULL, NULL } }


#define DECLARE_WAITQUEUE(name, tsk)					\
	wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)

DECLARE_WAITQUEUE(wait,current);
根據上面的宏不難看出,這裏將current放到了.private成員上。這裏還有一個指定的默認喚醒函數的指針。


接下來看一個add_wait_queue函數接口。看該函數之前要先看下wait_queue_head_t
struct __wait_queue_head {
	spinlock_t lock;
	struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
其實就是一個內核鏈表。將一連串的等待隊列連接起來而已,在修改鏈表的時候用lock鎖住。就這麼簡單

下面就看函數
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
	unsigned long flags;

	wait->flags &= ~WQ_FLAG_EXCLUSIVE;
	spin_lock_irqsave(&q->lock, flags);
	__add_wait_queue(q, wait);
	spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(add_wait_queue);


static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
{
	list_add(&new->task_list, &head->task_list);
}
該函數其實就是簡單的將等待隊列放到了鏈表中


請一定要記住上面那些代碼的流程,這裏複習下。。
1:將wait_queue_t通過list_add放到wait_queue_head_t內,wait_queue_t內包含當前的任務結構體current
2:通過宏__set_current_state(state_value)來改變任務狀態
3:然後調用schedule進行任務調度(由於當前任務狀態已經不是RUNING了,所以不會被調度執行)

下面來看一些wait_event相關函數。其實明白上面的流程之後很容易理解wait_event在做些什麼
#define wait_event(wq, condition) 					
do {	
	/* 首先判斷條件 */								
	if (condition)	 						
		break;
	/* 此處開始進入等待切換 */							
	__wait_event(wq, condition);					
} while (0)
#define __wait_event(wq, condition) 					
do {	
	/* 創建等待隊列 */								
	DEFINE_WAIT(__wait);						
									
	for (;;) {
		/* 切換任務狀態,並加入wait_queue_t到wait_queue_head_t */							
		prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);	
		/* 再一次判斷條件 */
		if (condition)						
			break;	
		/* 調度任務 */					
		schedule();						
	}
	/* 結束之後,將任務狀態再一次切換到TASK_RUNING 然後將wait_queue_t從wait_queue_head_t中移除 */								
	finish_wait(&wq, &__wait);					
} while (0)

帶有timeout超時的等待隊列,其基本相似,只是調度接口換了schedule_timeout而已,如下
#define wait_event_timeout(wq, condition, timeout)			
({									
	long __ret = timeout;						
	if (!(condition)) 						
		__wait_event_timeout(wq, condition, __ret);		
	__ret;								
})
#define __wait_event_timeout(wq, condition, ret)			
do {									
	DEFINE_WAIT(__wait);						
									
	for (;;) {							
		prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);	
		if (condition)						
			break;						
		ret = schedule_timeout(ret);				
		if (!ret)						
			break;						
	}								
	finish_wait(&wq, &__wait);					
} while (0)

這裏我不全部說明。具體的請參閱內核源碼
#define wait_event_interruptible(wq, condition)
#define wait_event_interruptible_timeout(wq, condition, timeout)

總結下

函數 調度函數 signal_pending
wait_event schedule N
wait_event_interruptible schedule Y
wait_event_timeout schedule_timeout N
wait_event_interruptible_timeout schedule_timeout Y

如何讓任務等待相信已經明白了吧。。下面該是喚醒的部分了,請看 淺談等待隊列的內部實現(二)

淺談等待隊列的內部實現(二)

http://blog.csdn.net/yyttiao/article/details/7877168


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章