阻塞型I/O,非阻塞型I/O,異步通知

一、ioctl

  1. ioctl系統調用可以實現用戶空間的各種需求,比如報告錯誤信息,改變波特率,執行自破壞等
  2. 用戶空間的ioctl(int fd, unsigned long cmd, …)後面的三個點表示的是可變參數目的參數表。這些點並不是數目不定的一串參數,而只是一個可選參數。另外這些點只是爲了在編譯時防止編譯器進行類型檢查。第三個參數的具體形式依賴於要完成的控制命令,也就是第二個參數。某些控制命令不需要參數,某些需要一個整型參數,而某些只需要一個指針參數。由於…躲過了編譯器,所以如果ioctl傳遞了一個非法參數,編譯器是無法報警的,這樣,相關聯的程序錯誤就很難發現。所以內核的ioctl使用swtich語句來解決這個問題,區分命令的類型。

二、阻塞I/O

1.概述

  • 對於用戶來說,調用read/write函數進行讀寫,而如果數據不可用時或者輸出緩衝區已滿,而調用進程通常不會關心這類問題,程序員只會簡單調用read,write,然後等待必要的工作結束後返回調用。因此,在這種情況下,我們的驅動程序應該(默認)阻塞該進程,將其置入休眠狀態直到請求可繼續。

2.休眠介紹

  • a.進程休眠的三條規則

    1.永遠不要在原子上下文中進入休眠。原子上下文指的是下面這種狀態:在執行多個步驟時,不能有任何的併發訪問,也即獨佔cpu,完成整個過程。所以休眠不能使用在擁有spinlock,seqlock或者RCU鎖時休眠,即使禁止了中斷,也不能休眠。擁有信號量的進程可以休眠,會導致其他等待該信號量的進程也會休眠,所以需保證擁有信號量而休眠的代碼必須短,並且還要確保擁有信號量並不會阻塞最終會喚醒我們自己的那個進程。

    2.當我們被喚醒時,永遠無法知道休眠了多久,或休眠期間發生了什麼事情,也不知道是否還有其他進程在同一事件上休眠,這個進程可能會在我們之前被喚醒並將我們等待的資源拿走。所以我們對喚醒之後的狀態也不能做任何假定,必須檢查以確保我們等待的條件真正爲真。

    3.除非我們知道會有其他進程在其他地方喚醒我們,否則進程不能休眠。完成喚醒任務的那個進程必須能找到我們的進程,這樣才能喚醒休眠的進程,所以能夠找到休眠的進程意味着,需要維護一個稱爲等待隊列的數據結構,等待隊列就是一個進程鏈表,其中包含了等待某個特定事件的所有進程。(可能很多等待隊列,每個等待隊列內的所有進程等待的事件都是一樣的,但是不能的等待隊列等待的事件不一樣)

3.簡單休眠

  • a.等待休眠的函數如下:

    wait_event(queue, condition)
    wait_event_interruptible()
    wait_event_timeout()
    wait_event_interruptible_timeout()
    queue是等待隊列頭,condition是一個條件表達式。這個函數會在進程休眠前後都要對condition求值。即在進程最後進入真正休眠的時候會再此檢查此條件是否爲真,如果爲假,那麼進程將真正的進入休眠。在進程真正休眠之後,若被喚醒並獲得處理器,會繼續檢查conditon是否爲真,在爲真之前,進程將保持睡眠。另外,timeout是隻要超時,進程將不再保持睡眠,此時不再考慮condition是否爲真。interruptible意味着在睡眠的過程中可被某個信號中斷,因此進程被喚醒後,需要判斷此函數的返回值。

  • b.整個休眠過程的另外一半是喚醒。其他的某個線程或者進程或者中斷處理例程必須爲我們喚醒。喚醒的函數爲:

    wake_up(wait_queue_head_t *queue)
    wake_up_interruptible()
    注意,wake_up會喚醒等待在queue上的所有進程,而wake_up_interruptible只會喚醒那些執行可中斷的睡眠進程,一般是配套使用,比如wait_event與wake_up,wait_event_interruptible與wake_up_interruptible.

  • c.休眠喚醒的條件併發竟態問題
    如下面額例子:
    sleepy_read()
    {
    wait_event_interruptible(wq, flag != 0)
    flag = 0;
    }

    sleepy_write()
    {
    flag = 1;
    wake_up_interruptible(&wq)
    }
    如果兩個調用sleepy_read()而等待睡眠的進程,當sleepy_write()被調用時,兩個等待睡眠的進程都會被喚醒。第一個被喚醒的進程(隨機的,不知道會被喚醒),會將flag重新置爲0,那麼第二個進程被喚醒後判斷flag仍然爲0(但是不知道資源被別人拿走了),所以將繼續保持休眠。那麼這裏就出現了竟態,因爲兩個被喚醒的進程會同時意識到flag被非零,但是隻有一個能獲取。所以結果就是隨機的,這裏引發的竟態問題將很難定位,所以需要確保只有一個進程能看到非零值,則必須以原子方式進行檢查。

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