前言
14. 非同步通知
本章內容為驅動基石之一。
驅動只提供功能,不提供策略。
阻塞與非阻塞是 APP 詢問 驅動裝置。
非同步通知是 驅動裝置 主動通知 APP。
原文:https://www.cnblogs.com/lizhuming/p/14918049.html
14.1 非同步通知的一些概念
非同步通知:一旦裝置就緒,則主動通知APP,這樣APP就不用輪詢查詢裝置狀態了。
非同步IO:APP 發起 IO 請求後,立即返回。然後再查詢 IO 完成情況,或者 IO 完成後被調回。這個過程叫做 非同步IO。
14.2 Linux 訊號
可以使用 訊號 來進行程式間通訊(IPC)。
Linux訊號表:
- 路徑參考:include\uapi\asm-generic\signal.h
訊號 | 值 | 說明 |
---|---|---|
SIGHUP | 1 | 掛起 |
SIGINT | 2 | 中斷中斷 |
SIGQUIT | 3 | 終端退出 |
SIGILL | 4 | 無效命令 |
SIGTRAP | 5 | 跟蹤陷阱 |
SIGABRT | 6 | 異常終止訊號,和 SIGIOT 同義 |
SIGIOT | 6 | IOT陷阱,和 SIGABRT 同義 |
SIGBUS | 7 | BUS錯誤 |
SIGFPE | 8 | 浮點異常 |
SIGKILL | 9 | 強制終止 |
SIGUSR1 | 10 | 使用者自定義訊號1 |
SIGSEGV | 11 | 無效的記憶體段處理 |
SIGUSR2 | 12 | 使用者自定義訊號2 |
SIGPIPE | 13 | 半關閉管道的寫操作 |
SIGALRM | 14 | 計時器到期 |
SIGTERM | 15 | 終止 |
SIGSTKFLT | 16 | 堆疊錯誤 |
SIGCHLD | 17 | 子程式已經停止或退出 |
SIGCONT | 18 | 如果停止了,繼續執行 |
SIGSTOP | 19 | 停止執行 |
SIGTSTP | 20 | 終端停止訊號 |
SIGTTIN | 21 | 後臺程式需要從終端讀取輸入 |
SIGTTOU | 22 | 後臺程式需要從終端寫出 |
SIGURG | 23 | 緊急的套接字事件 |
SIGXCPU | 24 | 超額使用CPU分配的時間 |
SIGXFSZ | 25 | 檔案尺寸超額 |
SIGVTALRM | 26 | 虛擬時鐘訊號 |
SIGPROF | 27 | 時鐘訊號描述 |
SIGWINCH | 28 | 出口尺寸變化 |
SIGIO | 29 | I/O |
SIGPOLL | SIGIO | I/O |
除了 SIGSTOP 和 SIGKILL 兩個訊號外,程式能夠忽略或捕獲其它所有訊號。
一個訊號被捕獲的意思是當一個訊號到達時有相應程式碼處理它。
如果一個訊號沒有被這程式所捕獲,核心將採取預設行為處理。
14.3 訊號接收
14.4 使用流程
14.4.1 參考流程圖
流程圖參考韋東山:
14.4.2 分析&程式設計步驟
分析中的細節部分會在後本節後面說明
分析:
-
②:繫結訊號與回撥函式。使用
sighandler_t signal(int signum, sighandler_t handler)
。 -
③:把 APP PID 告訴核心。同時,該 PID 會儲存到該驅動的核心檔案 file 結構體中。
-
④:讀取該驅動程式檔案的 Flag。
-
⑤:設定 Flag 裡面的 FASYNC 位為 1。當 FASYNC 位發生變化時,該驅動會呼叫驅動操作
drv_fasync
函式。 -
⑥:驅動開發者實現的函式。主要是呼叫 fasync_helper 函式。
-
⑦:呼叫 fasync_helper() 函式,主要是把 驅動程式核心檔案 file 結構體繫結到 button_async->fa_file 中。而 file 包含了 APP 的 PID。所以傳送訊號時,只需要使用 button_async 作為引數即可。
-
⑩:傳送訊號給對應的 APP。引數為 button_async。
-
注:button_async 結構體由驅動開發者建立,維護。
APP 訊號程式設計步驟:
- ①:編寫訊號回撥函式。
- ②:繫結訊號與回撥函式。
- ③:開啟驅動。
- ④:獲取 PID ,告知核心。
- ⑤:獲取程式狀態值。
- ⑥:當前狀態值新增非同步功能,觸發呼叫驅動非同步處理函式。
KERNEL 訊號程式設計步驟:
- ①:定義非同步結構體。
- ②:實現非同步操作函式,並把該函式填充到裝置核心驅動操作結構體中。
- 該函式內容主要呼叫 fasync_helper() 函式,初始化非同步結構體。(把 file ,核心PID,交給非同步結構體)
- ③:傳送訊號。
14.4.3 使用函式說明
14.4.3.1 相關結構體及參考模型
APP 模型 截段:
......
/* 設定訊號 SIGIO 的處理函式 */
signal(SIGIO, sigio_signal_func);
fcntl(fd, F_SETOWN, getpid()); /* 將當前程式的程式號告訴給核心 */
flags = fcntl(fd, F_GETFD); /* 獲取當前的程式狀態 */
fcntl(fd, F_SETFL, flags | FASYNC); /* 設定程式啟用非同步通知功能。會呼叫驅動中的 drv_fasync 函式 */
......
fasync_struct:
- (在核心原始碼中,目前沒有去找核心文件該結構體內容的相關資訊)
struct fasync_struct {
rwlock_t fa_lock;
int magic;
int fa_fd;
struct fasync_struct *fa_next; /* singly linked list */
struct file *fa_file;
struct rcu_head fa_rcu;
};
14.4.3.2 signal 函式
APP 使用。
函式原型:sighandler_t signal(int signum, sighandler_t handler)
:
- 功能:繫結訊號與回撥函式。
- signum:訊號型別。除 SIGKILL 和 SIGSTOP 外的任何一種訊號。
- handler:該引數有三種型別。
- ①:SIG_IGN 型別。表示忽略該訊號。
- ②:SIG_DFL 型別。表示恢復對訊號的系統預設處理。
- ③:sighandler_t 型別的函式指標。即是回撥函式。
typedef void (*sighandler_t)(int);
。
- 注:APP 收到訊號執行回撥函式時,signum 引數會被傳到回撥函式的形參傳遞給回撥函式。即是回撥函式的形參就是訊號型別。
14.4.3.3 kill_fasync 函式
KERNEL 使用。
函式原型:void kill_fasync(struct fasync_struct **fp, int sig, int band)
:
- 功能:傳送訊號給 fp 引數繫結的程式。(by PID)
- 參考路徑:linux-5.12.8\fs\fcntl.c
- fp:需要操作的 fasync_struct。
- sig:訊號型別。
- band:可讀時設定為 POLL_IN;可寫時設定為 POLL_OUT。當然該引數還可以填
POLL_MSG。以上三個值在應用層接收時,si_code 分別為 0x01、0x02、0x03。