非同步通知和MISC裝置驅動

lethe1203發表於2024-03-25

非同步通知:

驅動程式中的非同步通知是指驅動程式可以嚮應用程式傳送通知,告知應用程式發生了某種事件。這種通知是非同步的,即應用程式可以繼續執行其他操作,而不需要主動輪詢或等待事件的發生。
在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 來處理非同步通知。

相關文章