异步通知和MISC设备驱动

异步通知:

驱动程序中的异步通知是指驱动程序可以向应用程序发送通知,告知应用程序发生了某种事件。这种通知是异步的,即应用程序可以继续执行其他操作,而不需要主动轮询或等待事件的发生。
在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 来处理异步通知。
 
 
posted @ 2024-03-25 23:19  lethe1203  阅读(30)  评论(0编辑  收藏  举报