Linux之Wakeup&Callback機制

Linux內核的休眠與喚醒機制

在Linux內核中存在着等待隊列的數據結構,該數據結構是基於雙端鏈表實現,Linux內核通過將阻塞的進程任務添加到等待隊列中,而進程任務被喚醒則是在隊列輪詢遍歷檢測是否處於就緒狀態,如果是那麼會等待隊列中刪除等待節點並通過節點上的回調函數進行通知然後加入到cpu就緒隊列中等待cpu調度執行.其具體流程主要包含以下兩個處理邏輯,即休眠邏輯以及喚醒邏輯.

休眠邏輯

  • linux 內核休眠邏輯核心代碼
// 其中cmd = schedule(), 即一個調用schedule函數的指針
#define ___wait_event(wq_head, condition, state, exclusive, ret, cmd)
({										
	__label__ __out;							
	struct wait_queue_entry __wq_entry;					
	long __ret = ret;	/* explicit shadow */				
	// 初始化過程(內部代碼這裏省略,直接說明)
	// 1. 設置獨佔標誌到當前節點entry
	// 2. 將當前任務task指向節點的private
	// 3. 同時爲當前entry節點傳遞一個喚醒的回調函數autoremove_wake_function,一旦喚醒將會自動被刪除
	init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0);	
	for (;;) {
		// 防止隊列中沒有entry產生不斷的輪詢,主要處理wait_queue與entry節點添加或者刪除
		long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state);
		// 事件輪詢檢查是否事件有被喚醒							
		if (condition)							
			break;							
										
		if (___wait_is_interruptible(state) && __int) {			
			__ret = __int;						
			goto __out;						
		}								
		
		// 調用schedule()方法						
		cmd;								
	}
	// 事件被喚醒,將當前的entry從隊列中移除				
	finish_wait(&wq_head, &__wq_entry);					
__out:	__ret;									
})
  • 對此,我們可以總結如下:
    • 在linux內核中某一個進程任務task執行需要等待某個條件condition被觸發執行之前,首先會在內核中創建一個等待節點entry,然後初始化entry相關屬性信息,其中將進程任務存放在entry節點並同時存儲一個wake_callback函數並掛起當前進程
    • 其次不斷輪詢檢查當前進程任務task執行的condition是否滿足,如果不滿足則調用schedule()進入休眠狀態
    • 最後如果滿足condition的話,就會將entry從隊列中移除,也就是說這個時候事件已經被喚醒,進程處於就緒狀態

喚醒邏輯

  • linux內核的喚醒核心代碼
static int __wake_up_common(struct wait_queue_head *wq_head, unsigned int mode,
			int nr_exclusive, int wake_flags, void *key,
			wait_queue_entry_t *bookmark)
{
	// 省略其他非核心代碼...
	// 循環遍歷整個等待隊列
	list_for_each_entry_safe_from(curr, next, &wq_head->head, entry) {
		unsigned flags = curr->flags;
		int ret;

		if (flags & WQ_FLAG_BOOKMARK)
			continue;

		//調用喚醒的函數
		ret = curr->func(curr, mode, wake_flags, key);
		if (ret < 0)
			break;
		
		// 檢查當前節點是否爲獨佔節點,如果是,即nr_exclusive = 0退出循環遍歷
		if (ret && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
			break;

		if (bookmark && (++cnt > WAITQUEUE_WALK_BREAK_CNT) &&
				(&next->entry != &wq_head->head)) {
			bookmark->flags = WQ_FLAG_BOOKMARK;
			list_add_tail(&bookmark->entry, &next->entry);
			break;
		}
	}
	return nr_exclusive;
}

struct wait_queue_entry {
	unsigned int		flags;
	void			*private;
	// 這裏的func就是上述休眠的時候在init_wait_entry傳遞autoremove_wake_function
	wait_queue_func_t	func;	
	struct list_head	entry;
};

// 喚醒函數
int autoremove_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key)
{
	// 公用的喚醒函數邏輯
	// 內部執行try_to_wake_up, 也就是將wq_entry的private(當前進程)添加到cpu的執行隊列中,讓cpu能夠調度task執行
	int ret = default_wake_function(wq_entry, mode, sync, key);
	
	// 其他爲當前喚醒函數私有邏輯
	if (ret)
		list_del_init(&wq_entry->entry);

	return ret;
}
EXPORT_SYMBOL(autoremove_wake_function);
  • 對此,基於上述的喚醒邏輯可以總結如下:
    • 在等待隊列中循環遍歷所有的entry節點,並執行回調函數,直到當前entry爲排他節點的時候退出循環遍歷
    • 執行的回調函數中,存在私有邏輯與公用邏輯,類似模板方法設計模式
    • 對於default_wake_function的喚醒回調函數主要是將entry的進程任務task添加到cpu就緒隊列中等待cpu調度執行任務tas
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章