前言
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。