作業系統 再識 linux 等待佇列

2021-06-14 15:29:10 字數 4807 閱讀 1386

linux

核心的等待佇列是以雙迴圈鍊錶為基礎資料結構,與程序排程機制緊密結合,能夠用於實現核心的非同步事件通知機制。

在這個鍊錶中,有兩種資料結構:等待佇列頭(wait_queue_head_t)和等待佇列項(wait_queue_t)。等待佇列頭和等待佇列項中都包含乙個list_head型別的域作為"連線件"。它通過乙個雙鏈表和把等待tast的頭,和等待的程序列表鏈結起來。從上圖可以清晰看到。所以我們知道,如果要實現乙個等待佇列,首先要有兩個部分。佇列頭和佇列項。下面看他們的資料結構。

[cpp]

view plain

copy

1.structlist_head ;  

4.struct__wait_queue_head ;  

8.typedef

struct__wait_queue_head wait_queue_head_t;  

9.struct__wait_queue ;  

所以佇列頭和佇列項是通過

list_head

聯絡到一起的,

list_head

是乙個雙向鍊錶,在

linux

核心中有著廣泛的應用。並且在list.h中對它有著很多的操作。

2.對列頭和佇列項的初始化:

wait_queue_head_t my_queue;

init_waitqueue_head(&my_queue);

直接定義並初始化。init_waitqueue_head()函式會將自旋鎖初始化為未鎖,等待佇列初始化為空的雙向迴圈鍊錶。

declare_wait_queue_head(my_queue);

定義並初始化

3.定義等待佇列:declare_waitqueue(name,tsk);

[cpp]

view plain

copy

1.    

#define   declare_waitqueue(name,   tsk)       / 

2.     wait_queue_t   name     =__waitqueue_initializer(name,   tsk)   

3.    

4.    

#define   __waitqueue_initializer(name,   tsk)   ,  __waitqueue_debug_ini(name)} 

它的解釋是: 

通過declare_waitqueue巨集將等待佇列項初始化成對應的任務結構,並且用於連線的相關指標均設定為空。其中加入了除錯相關**。 

程序通過執行下面步驟將自己加入到乙個等待佇列中:

1) 呼叫declare_waitqueue()建立乙個等待佇列的項;2)

呼叫add_wait_queue()

把自己加入到等待佇列中。該佇列會在程序等待的條件滿足時喚醒它。在其他地方寫相關**,在事件發生時,對等的佇列執行

wake_up()

操作。3

)將程序狀態變更為:

task_interruptible ortask_uninterruptible。4

)如果狀態被置為

task_interruptible

,則訊號喚醒程序。即為偽喚醒(喚醒不是因為事件的發生),因此檢查並處理訊號。5)

檢查condition

是否為真,為真則沒必要休眠,如果不為真,則呼叫

scheduled()。6

)當程序被喚醒的時候,它會再次檢查條件是否為真。真就退出迴圈,否則再次呼叫

scheduled()

並一直重複這步操作。7)

condition

滿足後,程序將自己設定為

task_running

並通過remove_wait_queue()

退出。

4.(從等待佇列頭中)新增/移出等待佇列

(1)add_wait_queue()函式: (2)remove_wait_queue()函式:

5.等待事件:(有條件睡眠)

1)wait_event()巨集:

[cpp]

view plain

copy

1.    

#define wait_event(wq, condition) / 

2.    

3.dowhile(0)   

8.    

9.    

#define __wait_event_timeout(wq, condition, ret) / 

10. 

11.do/   

22.  finish_wait(&wq, &__wait); /   

23.  }while(0)   

在等待會列中睡眠直到

condition

為真。在等待的期間,程序會被置為

task_uninterruptible

進入睡眠,直到

condition

變數變為真。每次程序被喚醒的時候都會檢查

condition的值.

(2)wait_event_interruptible()函式:

和wait_event()

的區別是呼叫該巨集在等待的過程中當前程序會被設定為

task_interruptible狀態.

在每次被喚醒的時候

,首先檢查

condition

是否為真

,如果為真則返回

,否則檢查如果程序是被訊號喚醒

,會返回

-erestartsys

錯誤碼.

如果是condition為真,

則返回0.

(3)wait_event_timeout()巨集:

也與wait_event()類似.

不過如果所給的睡眠時間為負數則立即返回

.如果在睡眠期間被喚醒,且

condition

為真則返回剩餘的睡眠時間

,否則繼續睡眠直到到達或超過給定的睡眠時間

,然後返回0.

(4)wait_event_interruptible_timeout()巨集:

與wait_event_timeout()類似,

不過如果在睡眠期間被訊號打斷則返回

erestartsys

錯誤碼.

(5) wait_event_interruptible_exclusive()巨集

同樣和wait_event_interruptible()一樣,

不過該睡眠的程序是乙個互斥程序.

6.喚醒佇列:

(1)wake_up()函式:

喚醒等待佇列

.可喚醒處於

task_interruptible

和task_uninteruptible

狀態的程序,和

wait_event/wait_event_timeout

成對使用.

2)wake_up_interruptible()

函式: #definewake_up_interruptible(x) __wake_up(x, task_interruptible, 1, null) 和

wake_up()

唯一的區別是它只能喚醒

task_interruptible

狀態的程序.,與

wait_event_interruptible/wait_event_interruptible_timeout/wait_event_interruptible_exclusive

成對使用.

task_interruptible

,允許通過傳送

signal

喚醒它(即可中斷的睡眠狀態);

task_uninterruptible

,不接收任何

singal

7.在等待佇列上睡眠:(無條件睡眠,老核心使用,新核心建議不用)

(1)sleep_on()函式:

該函式的作用是定義乙個等待佇列(wait),並將當前程序新增到等待佇列中(wait),然後將當前程序的狀態置為task_uninterruptible,並將等待佇列(wait)新增到等待佇列頭(q)中。之後就被掛起直到資源可以獲取,才被從等待佇列頭(q)中喚醒,從等待佇列頭中移出。在被掛起等待資源期間,該程序不能被訊號喚醒。

(2)sleep_on_timeout()函式:

與sleep_on()函式的區別在於呼叫該函式時,如果在指定的時間內(timeout)沒有獲得等待的資源就會返回。實際上是呼叫schedule_timeout()函式實現的。值得注意的是如果所給的睡眠時間(timeout)小於0,則不會睡眠。該函式返回的是真正的睡眠時間。

(3)interruptible_sleep_on()函式:

該函式和sleep_on()函式唯一的區別是將當前程序的狀態置為task_interruptinle,這意味在睡眠如果該程序收到訊號則會被喚醒。

(4)interruptible_sleep_on_timeout()函式:

類似於sleep_on_timeout()函式。程序在睡眠中可能在等待的時間沒有到達就被訊號打斷而被喚醒,也可能是等待的時間到達而被喚醒。

作業系統 再識 Linux 驅動模型

1 底層機制 linux 的裝置驅動模型的底層機制主要包括 kobject,kobj type,kset 等幾個結構。這幾個 結構的定義在include linux kobject.h 中。1.1 kobject 代表裝置驅動模型中乙個基本物件,類似於mfc 中最頂層的基類cobject。每個kob...

作業系統 再識 Linux從檔案到字元裝置

在 dev下面有很多裝置,其中也有大家廣為使用的字元裝置,呢裡面的檔案是如何與字元裝置掛鉤的呢 平時我們使用的open函式是如何動態載入字元裝置的操作集的呢 下面就讓我們慢慢剖析 以核心2.6.26為參考 一.首先是檔案系統 需要動態解析檔案路徑名 像 dev ts0 在檔案系統裡分為3個部分 1....

作業系統 程序等待

程序等待的基礎概念程序等待就是為了同步父程序和子程序,如把運算放到子程序,賦值放到父程序,可能需要讓父程序等待子程序運算結束.乙個程序 在終止時會關閉所有的檔案描述符,釋放在使用者空間分配的記憶體,但他的pcb還保留著,核心在其中儲存了一些資訊 如果是正常終 止則儲存著退出狀態,如果是異常退出則儲存...