驅動篇 阻塞與非阻塞 I O(摘錄)

2021-10-10 17:04:51 字數 3766 閱讀 6236

阻塞與非阻塞

阻塞操作是指在執行裝置操作時若不能獲得資源則掛起程序,直到滿足可操作的條件後再進行操作。被掛起的程序進入休眠狀態,被從排程器的執行佇列移走,直到等待的條件被滿足。而非阻塞操作的程序在不能進行裝置操作時並不掛起,它或者放棄,或者不停地查詢,直至可以進行操作為止。

驅動程式通常需要提供這樣的能力: 當應用程式進行 read()、 write()等系統呼叫時,若裝置的資源不能獲取,而使用者又希望以阻塞的方式訪問裝置,驅動程式應在裝置驅動的 ***_read()、***_write()等操作中將程序阻塞直到資源可以獲取,此後,應用程式的 read()、write()等呼叫才返回,整個過程仍然進行了正確的裝置訪問,使用者並沒有感知到;若使用者以非阻塞的方式訪問裝置檔案,則當裝置資源不可獲取時,裝置驅動的 ***_read()、***_write()等操作應立即返回,read()、write()等系統呼叫也隨即被返回。

阻塞從字面上聽起來似乎意味著低效率,實則不然,如果裝置驅動不阻塞,則使用者想獲取裝置資源只能不停地查詢,這反而會無謂地耗費 cpu 資源。而阻塞訪問時,不能獲取資源的程序將進入休眠,它將 cpu 資源讓給其他程序。因為阻塞的程序會進入休眠狀態,因此,必須確保有乙個地方能夠喚醒休眠的程序。喚醒程序的地方最大可能發生在中斷裡面,因為硬體資源獲得的同時往往伴隨著乙個中斷。

以阻塞和非阻塞方式讀取串列埠乙個字元的**。實際的串列埠程式設計中,若使用非阻塞模式,還可借助訊號(sigaction)以非同步方式訪問串列埠以提高 cpu 利用率,而這裡僅僅是為了說明阻塞與非阻塞的區別。

阻塞地讀取串列埠乙個字元

char buf;

fd =

open

("/dev/ttys1"

, o_rdwr);.

..res =

read

(fd,

&buf,1)

;//當串列埠上有輸入時才返回

if(res==1)

printf

("%c\n"

, buf)

;

非阻塞地讀取串列埠乙個字元

char buf;

fd =

open

("/dev/ttys1"

, o_rdwr| o_nonblock);.

..while

(read

(fd,

&buf,1)

!=1);

//串列埠上無輸入也返回,所以要迴圈嘗試讀取串列埠

printf

("%c\n"

, buf)

;

等待佇列

可以使用等待佇列(wait queue)來實現阻塞程序的喚醒。wait queue 很早就作為乙個基本的功能單位出現在 linux 核心裡了,它以隊列為基礎資料結構,與程序排程機制緊密結合,能夠用於實現核心中的非同步事件通知機制。等待佇列可以用來同步對系統資源的訪問,訊號量在核心中也依賴等待佇列來實現。

1.定義「等待佇列頭」

wait_queue_head_t my_queue;
2.初始化「等待佇列頭」

init_waitqueue_head

(&my_queue)

;而下面的 declare_wait_queue_head

()巨集可以作為定義並初始化等待佇列

頭的「快捷方式」

declare_wait_queue_head (name)

3.定義等待佇列。

declare_waitqueue

(name, tsk)

該巨集用於定義並初始化乙個名為 name 的等待佇列。

4.新增/移除等待佇列。

void fastcall add_wait_queue

(wait_queue_head_t *q,wait_queue_t *wait)

;void fastcall remove_wait_queue

(wait_queue_head_t *q, wait_queue_t *wait)

;add_wait_queue

()用於將等待佇列 wait 新增到等待佇列頭 q 指向的等待佇列鍊錶中,

而 remove_wait_queue

()用於將等待佇列 wait 從附屬的等待佇列頭 q 指向的等待隊

列煉表中移除。

5.等待事件。

wait_event

(queue, condition)

wait_event_interruptible

(queue, condition)

wait_event_timeout

(queue, condition, timeout)

wait_event_interruptible_timeout

(queue, condition, timeout)

等待第乙個引數 queue 作為等待佇列頭的等待佇列被喚醒,而且第二個引數

condition 必須滿足,否則阻塞。wait_event

()和 wait_event_interruptible

()的區別在於後

者可以被訊號打斷,而前者不能。加上_timeout 後的巨集意味著阻塞等待的超時時間,

以 jiffy 為單位,在第三個引數的 timeout 到達時,不論 condition 是否滿足,均返回。

6.喚醒佇列

void

wake_up

(wait_queue_head_t *queue)

;void

wake_up_interruptible

(wait_queue_head_t *queue)

;上述操作會喚醒以 queue 作為等待佇列頭的所有等待佇列中所有屬於該等待佇列

頭的等待佇列對應的程序

wake_up() 應 與 wait_event() 或 wait_event_timeout() 成 對 使 用 , 而wake_up_interruptible()則應與wait_event_interruptible()或wait_event_interruptible_timeout() 成 對 使 用 。 wake_up() 可 喚 醒 處 於task_interruptible 和 task_uninterruptible 的 進 程 , 而wake_up_interruptible()只能喚醒處於 task_interruptible 的程序。

7.在等待佇列上睡眠。

sleep_on

(wait_queue_head_t *q )

;interruptible_sleep_on

(wait_queue_head_t *q )

;sleep_on

()函式的作用就是將目前程序的狀態置成 task_uninterruptible,

並定義乙個等待佇列,之後把它附屬到等待佇列頭 q,直到資源可獲得,q 引導的等

待佇列被喚醒。

interruptible_sleep_on()與 sleep_on()函式類似,其作用是將目前程序的狀態置成task_ interruptible,並定義乙個等待佇列,之後把它附屬到等待佇列頭 q,直到資源可獲得,q 引導的等待佇列被喚醒或者程序收到訊號。sleep_on() 函 數 應 該 與 wake_up() 成 對 使 用 ,interruptible_sleep_on() 應 該 與wake_up_interruptible()成對使用。

阻塞IO與非阻塞IO

阻塞io,當前程序因不滿足一些條件,而被掛起,即阻塞,cpu改去服務其它程序,read乙個普通檔案,就馬上執行,read乙個滑鼠,可是滑鼠沒有動,於是就阻塞了,阻塞的好處,利於os效能的發揮,cpu發揮高,雖然個體的費了點時間,但是總的效率得到了提高,阻塞在多路io的時候,缺陷就出來了,比如2路io...

非阻塞IO與阻塞IO

非阻塞式呼叫的問題 kibuv提供了乙個執行緒池 阻塞於非阻塞對於被呼叫者,即系統層面,系統為程式提供了阻塞呼叫和非阻塞呼叫,同步和非同步是對於呼叫者,就是自己的程式,發七呼叫,沒有其他操作,只是等待結果這個過程就是同步,發起呼叫後會等待結果,繼續完成其他的工作,等有回掉再執行,這個過程就是非同步的...

阻塞與非阻塞I O

還記得上篇 我們講到的是linux中併發控制訪問的手段有哪些?原子 訊號量 自旋鎖 互斥體。這是為了保護臨界區的資源,是多個程序對共享資源的併發訪問的一種處理手段。但是,在驅動程式中,我們常常為了支援使用者空間對裝置的靈活訪問,引入了阻塞與非阻塞i o兩種不同模式。阻塞操作是指在執行裝置操作時若不能...