阻塞與非阻塞
阻塞操作是指在執行裝置操作時若不能獲得資源則掛起程序,直到滿足可操作的條件後再進行操作。被掛起的程序進入休眠狀態,被從排程器的執行佇列移走,直到等待的條件被滿足。而非阻塞操作的程序在不能進行裝置操作時並不掛起,它或者放棄,或者不停地查詢,直至可以進行操作為止。
驅動程式通常需要提供這樣的能力: 當應用程式進行 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兩種不同模式。阻塞操作是指在執行裝置操作時若不能...