非同步通知:
驅動程式中的非同步通知是指驅動程式可以嚮應用程式傳送通知,告知應用程式發生了某種事件。這種通知是非同步的,即應用程式可以繼續執行其他操作,而不需要主動輪詢或等待事件的發生。
在Linux核心中,實現驅動非同步通知的一種常見方式是使用fasync機制。這個機制的核心是利用fasync_helper函式和kill_fasync函式來實現驅動程式和應用程式之間的非同步通訊。
- fasync_helper函式:
在驅動程式中,透過呼叫fasync_helper函式可以將應用程式的PID新增到驅動程式的非同步通知列表中,從而告知核心在特定事件發生時通知這些應用程式。
- kill_fasync函式:
當驅動程式需要嚮應用程式傳送通知時,可以呼叫kill_fasync函式向所有在非同步通知列表中的應用程式傳送訊號。
應用程式在接收到訊號後,可以註冊訊號處理程式來處理非同步通知,從而實現在事件發生時做出相應的處理
fsync函式說明:
int fasync_helper(int fd, struct file *filp, int on, struct fasync_struct **fapp); // 引數說明 fd:檔案描述符,指定要進行非同步通知的檔案描述符。 filp:指向表示檔案的結構體指標,即struct file結構體的指標。 on:指定是否開啟非同步通知,非零值表示開啟,零值表示關閉。 fapp:指向指標的指標,用於操作非同步通知佇列。 // 返回值說明 成功,返回0;失敗,返回負數錯誤碼,如-EINVAL等。 int kill_fasync(struct fasync_struct **fapp, int sig, int poll_event); // 引數說明 fapp:指向指標的指標,用於操作非同步通知佇列。 sig:要傳送的訊號型別,通常是SIGIO。 poll_event:指定事件型別,通常是POLL_IN(輸入事件)或POLL_OUT(輸出事件)。 // 返回值說明 成功,返回0;失敗,返回負數錯誤碼,如-EINVAL等。
MISC裝置驅動:
MISC 驅動叫做雜項驅動,就是開發板上的某些外設無法進行分類的時候就可以使用 MISC 驅動。所有的 MISC 裝置驅動的主裝置號都為 10,不同的裝置使用不同的從裝置號。隨著 Linux字元裝置驅動的不斷增加,裝置號變得越來越緊張,尤其是主裝置號,MISC 裝置驅動就用於解決此問題。MISC 裝置會自動建立 cdev。
int minor; // 次裝置號,用於標識裝置 const char *name; // 裝置名稱 const struct file_operations *fops; // 裝置檔案操作 struct list_head list; // 裝置連結串列頭 struct device *parent; // 父裝置指標 struct device *this_device; // 當前裝置指標 const struct attribute_group **groups; // 屬性組指標陣列 const char *nodename; // 裝置節點名稱 umode_t mode; // 裝置訪問許可權模式 };
測試demo:
驅動測試demo:
#include <linux/module.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/uaccess.h> #include <linux/sched.h> static struct miscdevice fasync_device; static int fasync_demo_fasync(int fd, struct file *filp, int mode) { return fasync_helper(fd, filp, mode, &fasync_device.this_device->async_queue); } static ssize_t fasync_demo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { // 在此示例中,read函式僅作為佔位符,不做實際操作 return 0; } static ssize_t fasync_demo_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { // 觸發非同步通知 kill_fasync(&fasync_device.this_device->fasync_list, SIGIO, POLL_OUT); return count; } static const struct file_operations fasync_demo_fops = { .owner = THIS_MODULE, .read = fasync_demo_read, .write = fasync_demo_write, .fasync = fasync_demo_fasync, // 將fasync函式指標指向實現的fasync函式 }; static int __init fasync_demo_init(void) { fasync_device.minor = MISC_DYNAMIC_MINOR; fasync_device.name = "fasync_demo_device"; fasync_device.fops = &fasync_demo_fops; misc_register(&fasync_device); printk(KERN_INFO "Fasync demo device registered\n"); return 0; } static void __exit fasync_demo_exit(void) { misc_deregister(&fasync_device); printk(KERN_INFO "Fasync demo device unregistered\n"); } module_init(fasync_demo_init); module_exit(fasync_demo_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("lethe1203"); MODULE_DESCRIPTION("Fasync && misc demo");
使用者程式測試demo:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <signal.h> #define DEVICE_FILE "/dev/fasync_demo_device" void signal_handler(int signo) { printf("Asynchronous notification received!\n"); } int main() { int fd; struct sigaction sa; // 設定訊號處理程式 sa.sa_handler = signal_handler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGIO, &sa, NULL); // 開啟裝置檔案 fd = open(DEVICE_FILE, O_RDWR); if (fd < 0) { perror("Failed to open the device..."); return -1; } // 將程序設定為接收SIGIO訊號 fcntl(fd, F_SETOWN, getpid()); // 開啟非同步通知 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_ASYNC); // 寫入資料以觸發非同步通知 write(fd, "Trigger asynchronous notification", sizeof("Trigger asynchronous notification")); // 等待非同步通知 pause(); // 關閉裝置檔案 close(fd); return 0; }
在上面demo中,在驅動程式中使用fasync_helper函式來設定非同步通知,並在應用程式中使用fcntl和訊號處理程式來處理非同步通知。當應用程式寫入資料到裝置檔案時,會觸發非同步通知,驅動程式透過kill_fasync函式傳送SIGIO訊號通知應用程式。應用程式接收到SIGIO訊號後,呼叫訊號處理程式 signal_handler 來處理非同步通知。