【嵌入式Linux驅動開發】十、總結按鍵讀取方法,初探異步通知概念

   生活不能等待別人來安排,要自己去爭取和奮鬥;而不論其結果是喜是悲,但可以慰藉的是,你總不枉在這世界上活了一場。有了這樣的認識,你就會珍重生活,而不會玩世不恭;同時,也會給人自身注入一種強大的內在力量。


通過本節可以瞭解到如下知識:

① 驅動的基本技能:中斷、休眠、喚醒、 poll 等機制。

  • 這些基本技能是驅動開發的基礎,其他大型驅動複雜的地方是它的框架及設計思想,但是基本技術就這些。

② APP開發的基本技能:阻塞 、非阻塞、休眠、 poll、異步通知。

一、按鍵讀取方法

APP 去讀取按鍵也有 4 種方法:

  • ① 查詢方式
  • ② 休眠-喚醒方式(只能幹這一件事)
  • ③ poll 方式
  • ④ 異步通知方式

  第 2、 3、 4 種方法,都涉及中斷服務程序。中斷,就像小孩醒了會哭鬧一樣,中斷不經意間到來,它會做某些事情:喚醒 APP、向 APP 發信號。所以,在按鍵驅動程序中,中斷是核心。實際上,中斷無論是在單片機還是在 Linux 中都很重要。在 Linux 中,中斷的知識還涉及進程、線程等。

1.1、查詢方式

A
  驅動程序中構造、註冊一個 file_operations 結構體,裏面提供有對應的 open,read 函數。
  APP 調用 open 時,導致驅動中對應的 open 函數被調用,在裏面配置 GPIO 爲輸入引腳。APP 調用 read 時,導致驅動中對應的 read 函數被調用,它讀取寄存器,把引腳狀態直接返回給 APP。

1.2、休眠-喚醒方式

在這裏插入圖片描述
  驅動程序中構造、註冊一個 file_operations 結構體,裏面提供有對應的 open,read 函數。
  APP 調用 open 時,導致驅動中對應的 open 函數被調用,在裏面配置 GPIO 爲輸入引腳;並且註冊 GPIO 的中斷處理函數。APP 調用 read 時,導致驅動中對應的 read 函數被調用, 如果有按鍵數據則直接返回給APP;否則 APP 在內核態休眠
  當用戶按下按鍵時, GPIO 中斷被觸發,導致驅動程序之前註冊的中斷服務程序被執行。它會記錄按鍵數據,並喚醒休眠中的 APP。APP 被喚醒後繼續在內核態運行,即繼續執行驅動代碼,把按鍵數據返回給 APP(用戶空間)。

1.3、 poll 方式

在這裏插入圖片描述
  上面的休眠-喚醒方式有個缺點:如果用戶一直沒操作按鍵,那麼 APP 就會永遠休眠。我們可以給 APP 定個鬧鐘,這就是 poll 方式。

  驅動程序中構造、註冊一個 file_operations 結構體,裏面提供有對應的 open,read,poll函數。
  APP 調用 open 時,導致驅動中對應的 open 函數被調用,在裏面配置 GPIO 爲輸入引腳;並且註冊 GPIO 的中斷處理函數
  APP 調用 poll 或 select 函數,意圖是“查詢”是否有數據,這 2 個函數都可以指定一個超時時間,即在這段時間內沒有數據的話就返回錯誤。這會導致驅動中對應的 poll 函數被調用,如果有按鍵數據則直接返回給 APP;否則 APP 在內核態休眠一段時間。
  當用戶按下按鍵時, GPIO 中斷被觸發,導致驅動程序之前註冊的中斷服務程序被執行。它會記錄按鍵數據,並喚醒休眠中的 APP。如果用戶沒按下按鍵,但是超時時間到了,內核也會喚醒 APP。
  所以 APP 被喚醒有 2 種原因:用戶操作了按鍵,超時。被喚醒的 APP 在內核態繼續運行,即繼續執行驅動代碼,把“狀態”返回給 APP(的用戶空間)。APP 得到poll/select 函數的返回結果後,如果確認是有數據的,則再調用 read 函數,這會導致驅動中的 read 函數被調用,這時驅動程序中含有數據,會直接返回數據。

1.4、異步通知方式

在這裏插入圖片描述

  異步通知的實現原理是:內核給 APP 發信號。信號有很多種,這裏發的是 SIGIO(IO類型的信號)。

  驅動程序中構造、註冊一個 file_operations 結構體,裏面提供有對應的open,read,fasync函數。
  APP 調用 open 時,導致驅動中對應的 open 函數被調用,在裏面配置 GPIO 爲輸入引腳;並且註冊 GPIO 的中斷處理函數。APP 給信號 SIGIO 註冊自己的處理函數: my_signal_fun。
  APP 調用 fcntl 函數, 把驅動程序的 flag 改爲 FASYNC,這會導致驅動程序的 fasync 函數被調用,它只是簡單記錄進程 PID。
  當用戶按下按鍵時, GPIO 中斷被觸發,導致驅動程序之前註冊的中斷服務程序被執行。它會記錄按鍵數據, 然後給進程 PID 發送 SIGIO 信號。
  APP 收到信號後會被打斷,先執行信號處理函數:在信號處理函數中可以去調用 read函數讀取按鍵值。
  信號處理函數返回後, APP 會繼續執行原先被打斷的代碼

1.5、按鍵讀取總結

  我們的驅動程序可以實現上述 4 種提供按鍵的方法,但是驅動程序不應該限制 APP 使用哪種方法。
  這就是驅動設計的一個原理:提供能力,不提供策略。就是說,你想用哪種方法都行,驅動程序都可以提供;但是驅動程序不能限制你使用哪種方法。

二、異步通知的實例測試,加深理解

signal.c

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void my_sig_func(int signo)
{
    printf("get a signal : %d\n", signo);
}

int main(int argc, char **argv)
{
    int i = 0;
    
    signal(SIGIO, my_sig_func);
    
    while (1) 
    {
        printf("Hello, world %d!\n", i++);
        sleep(2);
    }
    
    return 0;
}

程序第 13 行註冊信號處理函數,第 15 行就是一個無限循環。在它運行期間,你可以用另一個 APP 發信號給它,來查看異步通知的效果。

  • ①、使用gcc編譯該程序
gcc -o signal signal.c // 編譯程序
  • ②、運行該程序
./signal

可以看到,一直在打印
在這裏插入圖片描述
再開啓一個終端,向該APP發送信號

  • ③、查看signal的進程ID
  941 pts/2    00:00:00 signal

我這邊顯示是941
在這裏插入圖片描述

  • ④、在第二個終端發送信號
kill -SIGIO 941

在這裏插入圖片描述
可以看到,第一個終端收到了信號,並在繼續運行。

當我們屏蔽signal.c中的signal(SIGIO, my_sig_func);,再來發送信號,由會怎麼樣呢?按ctrl+c結束當前終端打印,重複上述①②③④步。

在這裏插入圖片描述

可以看到,發送信後直接殺死了第一個終端,說明此時沒有了信號異步通知。

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