首页 > 系统相关 >linux驱动开发-内核异步通知

linux驱动开发-内核异步通知

时间:2024-09-17 18:24:04浏览次数:10  
标签:异步 major param fasync 内核 linux mydevice 设备

驱动/内核异步通知

在Linux驱动程序中,

异步通知机制允许内核模块在特定事件发生时主动通知用户空间进程。

这样的设计通常用于管理输入输出操作,

确保用户程序可以及时获得数据或状态变化而无需频繁查询设备状态。


在Linux驱动中,常见的异步通知机制主要有以下几种:

信号(Signals):通过信号机制,内核可以向用户空间的进程发送通知(如SIGIO)。
等待队列(Wait Queues):允许进程在条件未满足时进入睡眠状态,而在条件满足时被唤醒。
文件描述符的事件通知(non-blocking I/O 和 poll):通过重载poll或select等操作,使得用户空间可以通过轮询的方式获知I/O事件的发生。

使用异步通知的好处

效率:通过非阻塞I/O和轮询的方法,可以有效降低系统开销。

实时性:驱动程序可以在数据可用时立即通知用户进程,减少等待时间。

简化操作:用户空间不需要轮询检查设备状态,可以直接过来响应事件。

示例

#include <linux/module.h>       // 包含模块的基本定义
#include <linux/kernel.h>       // 包含内核相关的基本函数
#include <linux/fs.h>           // 包含文件操作相关的结构体与函数
#include <linux/cdev.h>         // 包含字符设备的相关操作
#include <linux/uaccess.h>      // 用户空间与内核空间数据交换的函数
#include <linux/signal.h>       // 信号相关的定义与函数
#include <linux/sched.h>        // 进程管理相关的结构体与函数

#define DEVICE_NAME "mydevice"  // 设备名称
#define CLASS_NAME "myclass"    // 设备类名称

static int major_number;  // 主设备号
static struct class* myclass = NULL;  // 设备类结构体
static struct device* mydevice = NULL;  // 设备结构体
static struct cdev mycdev;  // 字符设备结构体
static struct fasync_struct *fasync_queue = NULL;  // 异步通知队列

// 设备打开函数
static int mydevice_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO "mydevice: device opened\n");  // 打开设备时打印信息
    return 0;  // 成功打开设备
}

// 设备关闭函数
static int mydevice_release(struct inode *inode, struct file *file) {
    printk(KERN_INFO "mydevice: device closed\n");  // 关闭设备时打印信息
    return 0;  // 成功关闭设备
}

// 设备读取函数
static ssize_t mydevice_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) {
    printk(KERN_INFO "mydevice: read operation\n");  // 读取设备时打印信息
    return 0;  // 在此示例中,读取操作不返回任何数据
}

// 设备写入函数
static ssize_t mydevice_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset) {
    printk(KERN_INFO "mydevice: write operation\n");  // 写入设备时打印信息

    // 模拟设备状态变化,发送SIGIO信号给异步通知队列
    if (fasync_queue) {
        // 发送异步通知信号
        //@param fasync_queue: 异步通知队列
        // @param signal: 信号
        // @param state: 状态
        kill_fasync(&fasync_queue, SIGIO, POLL_IN);  // 发送异步通知信号
    }

    return length;  // 返回写入的数据长度
}

// 异步通知设置函数
//@param fd: 文件描述符
//@param filp: 文件指针
//@param on: 注册或注销标志
//@param fasync_queue: 异步通知队列
// 返回值: 成功返回0,失败返回-EIO
static int mydevice_fasync(int fd, struct file *filp, int on) {
    // 注册或注销异步通知
    //@todo: 实现异步通知
    //@param fd: 文件描述符
    //@param filp: 文件指针
    //@param on: 注册或注销标志
    //@param fasync_queue: 异步通知队列
    // 返回值: 成功返回0,失败返回-EIO
    if (fasync_helper(fd, filp, on, &fasync_queue) >= 0) {  // 设置异步通知
        printk(KERN_INFO "mydevice: fasync setup successful\n");  // 设置成功时打印信息
        return 0;  // 成功
    }
    return -EIO;  // 设置失败,返回错误
}

// 文件操作结构体
static struct file_operations fops = {
    .owner = THIS_MODULE,       // 模块所有者
    .open = mydevice_open,      // 打开设备的函数指针
    .release = mydevice_release, // 关闭设备的函数指针
    .read = mydevice_read,      // 读取设备的函数指针
    .write = mydevice_write,    // 写入设备的函数指针
    .fasync = mydevice_fasync,  // 异步通知函数的指针
};

// 模块初始化函数
static int __init mydevice_init(void) {
    // 注册字符设备
    // @param major_number: 主设备号
    // @param DEVICE_NAME: 设备名称
    // @param fops: 文件操作结构体
    // 返回值: 成功返回主设备号,失败返回错误号
    major_number = register_chrdev(0, DEVICE_NAME, &fops);  // 注册字符设备
    if (major_number < 0) {
        printk(KERN_ALERT "mydevice: failed to register a major number\n");  // 注册失败打印警告
        return major_number;  // 返回错误号
    }
    printk(KERN_INFO "mydevice: registered correctly with major number %d\n", major_number);  // 打印注册成功信息

    // 创建设备类
    // @param THIS_MODULE: 当前模块
    // @param CLASS_NAME: 设备类名称
    // 返回值: 成功返回设备类结构体,失败返回错误号
    myclass = class_create(THIS_MODULE, CLASS_NAME);  // 创建设备类
    if (IS_ERR(myclass)) {
        // 销毁设备类
        unregister_chrdev(major_number, DEVICE_NAME);  // 注销设备号
        printk(KERN_ALERT "mydevice: failed to register device class\n");
        return PTR_ERR(myclass);  // 返回错误号
    }
    printk(KERN_INFO "mydevice: device class registered correctly\n");  // 打印设备类注册成功信息

    // 创建设备
    // @param myclass: 设备类结构体
    // @param NULL: 父设备
    // @param MKDEV(major_number, 0): 设备号
    // @param NULL: 设备参数
    // @param DEVICE_NAME: 设备名称
    // 返回值: 成功返回设备结构体,失败返回错误号
    mydevice = device_create(myclass, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME);  // 创建设备
    if (IS_ERR(mydevice)) {
        class_destroy(myclass);  // 销毁设备类
        unregister_chrdev(major_number, DEVICE_NAME);  // 注销设备号
        printk(KERN_ALERT "mydevice: failed to create the device\n");
        return PTR_ERR(mydevice);  // 返回错误号
    }
    printk(KERN_INFO "mydevice: device class created correctly\n");  // 打印设备创建成功信息

    // 初始化字符设备
    // @param &mycdev: 字符设备结构体
    // @param &fops: 文件操作结构体
    cdev_init(&mycdev, &fops);  // 初始化字符设备
    mycdev.owner = THIS_MODULE;  // 设置设备所有者
    // 添加字符设备
    // @param &mycdev: 字符设备结构体
    // @param MKDEV(major_number, 0): 设备号
    // @param 1: 设备数量
    cdev_add(&mycdev, MKDEV(major_number, 0), 1);  // 添加字符设备

    return 0;  // 初始化成功
}

// 模块退出函数
static void __exit mydevice_exit(void) {
    if (fasync_queue) {
        fasync_helper(-1, NULL, 0, &fasync_queue);  // 清除异步通知队列
    }
    device_destroy(myclass, MKDEV(major_number, 0));  // 销毁设备
    class_unregister(myclass);  // 注销设备类
    class_destroy(myclass);  // 销毁设备类
    unregister_chrdev(major_number, DEVICE_NAME);  // 注销字符设备
    printk(KERN_INFO "mydevice: Goodbye from the LKM!\n");  // 模块退出时打印信息
}

module_init(mydevice_init);  // 模块初始化入口
module_exit(mydevice_exit);  // 模块退出入口

MODULE_LICENSE("GPL");  // 模块许可证
MODULE_AUTHOR("gopher");  // 模块作者
MODULE_DESCRIPTION("A simple character device driver with async notification");  // 模块描述
MODULE_VERSION("1.0");  // 模块版本

测试

#include <stdio.h>       // 包含标准输入输出库
#include <stdlib.h>      // 包含标准库,提供通用的函数
#include <fcntl.h>       // 包含文件控制相关的定义
#include <unistd.h>      // 包含与Unix标准的函数
#include <signal.h>      // 包含信号处理相关的定义
#include <string.h>      // 包含字符串处理相关的函数

#define DEVICE_FILE "/dev/mydevice"  // 定义设备文件路径

// 信号处理函数
void sigio_handler(int signo) {
    // 当接收到SIGIO信号时调用该函数
    if (signo == SIGIO) {  // 检查信号是否为SIGIO
        printf("Received SIGIO signal\n");  // 打印接收到的信号信息
    }
}

int main() {
    int fd;  // 文件描述符
    int flags;  // 文件状态标志

    // 设置SIGIO信号处理函数
    // @note: 信号处理函数的第一个参数是信号值,第二个参数是信号处理函数的指针
    // @note: 这里将信号处理函数设置为sigio_handler
    signal(SIGIO, sigio_handler);  // 设置信号处理函数,当接收到SIGIO信号时,调用sigio_handler

    // 打开设备文件,进行读写操作
    fd = open(DEVICE_FILE, O_RDWR);  
    if (fd == -1) {  // 检查打开是否成功
        perror("Failed to open the device");  // 如果失败,打印错误信息
        return -1;  // 返回错误代码
    }

    // 设置当前进程为文件描述符的拥有者,以接收信号
    fcntl(fd, F_SETOWN, getpid());  
    // 获取当前文件描述符的状态标志
    //@note: F_GETFL获取文件状态标志,F_SETFL设置文件状态标志
    // 这里设置文件描述符为异步通知模式,以便接收SIGIO信号
    flags = fcntl(fd, F_GETFL);
    // 设置文件描述符为异步通知模式,以便接收SIGIO信号
    // @note: FASYNC表示异步通知模式,F_SETFL设置文件状态标志
    // @note: 这里使用位或运算符“|”将FASYNC与flags进行或运算,得到新的flags值
    // @note: 新的flags值将会应用到文件描述符上,从而使得文件描述符变为异步通知模式
    // @note: 异步通知模式下,文件描述符上发生的事件(如读写等)将会触发SIGIO信号
    // @note: 信号处理函数sigio_handler将会被调用,处理异步通知事件
    fcntl(fd, F_SETFL, flags | FASYNC);  

    printf("Waiting for SIGIO signal...\n");  // 提示用户正在等待SIGIO信号
    while (1) {  // 主循环
        sleep(1);  // 每次循环休眠1秒
    }

    close(fd);  // 关闭设备文件(这部分实际上是不会执行到的,因为上面的循环是无限的)
    return 0;  // 返回0,表示程序正常退出
}

标签:异步,major,param,fasync,内核,linux,mydevice,设备
From: https://blog.csdn.net/gopher9511/article/details/142317099

相关文章

  • 【python学习】深入掌握 Python RQ 任务队列库:全面处理异步任务的实战指南
    引言rq是基于Redis的Python任务队列库,用于处理异步任务。它能帮助开发者将繁重的后台任务交由独立进程执行,从而提高系统性能。在复杂项目中,任务的超时、重试、定时执行、依赖关系以及队列优先级等功能尤为重要。本文将全面介绍rq的常用和高级功能,帮助你在项目中灵活......
  • linux用户管理基本命令
    目录禁止非root用户连接配置文件/etc/group/etc/gshadow/etc/passwd/etc/shadow用户组管理groupaddgroupmodgroupdel用户管理useraddusermoduserdelpasswd主要组和附属组gpasswdnewgrpsuidgroups禁止非root用户连接在etc目录下创建nologin的空文件即可touch/etc/nologin配置......
  • 【Linux 20】线程控制
    文章目录......
  • Linux基础命令
    一、嵌入式、Linux背景嵌入式:硬件与软件相结合定制、为硬件设计相关代码来进行操作,代码测试,烧进板子,通过语音、图像、按钮等操作方式来调用。操作系统种类:DosWindowsUnixMacChmod osAndroid->华为、小米、中兴等等iOS介绍一下Linux:结构:一棵倒置的树/bin   ......
  • Linux系统与服务构建运维
    使用ext4文件系统格式化逻辑卷mylv。命令如下:一、Linux操作系统安装1.学习目标(1)了解服务器操作系统安装。(2)了解CentOS系统的安装。2.节点规划IP主机名节点192.168.200.10localhostLinux服务器节点3.基础准备使用本地PC环境的VMWareWorkstation软件进行实......
  • Linux实用操作
    文章目录Linux实用操作各种实用小技巧软件安装systemctl软链接日期和时区日期时区ntpIP和主机名ip地址主机名配置VMware固定IP进程ps命令kill命令端口nmap命令netstat命令网络请求和下载ping命令wget命令curl命令主机状态监控top命令df命令iostat命令sar命令环境变......
  • Centos7完美平替,Almalinux系统(低配版)安装oracle
            目前Centos7官网已停止维护,其官方下载源不能使用,在经过网上各类教程的洗礼之后,我放弃了给它更换国内的下载源(换完了也不行(小声bb))。于是我想到了是否可以找一个可以兼容Centos7的平替系统,即Almalinux。    本文主要介绍在低配版Almalinux8.9系统(如云服......
  • QEMU on Linux hosts(By frp)
    Invocation—QEMUdocumentationHosts/Linux-QEMU关键字:QEMU、Tips:由于是使用反向代理frp 内网穿透在无图形界面的Ubuntu24.04LTS主机,通过ssh安装QEMU,频繁出现掉线问题,所以使用Screenapt-getinstallscreenroot@atc:~#screen-vScreenversion4.09.01(GNU)20-Au......
  • 【Linux进程】Linux Shell编程实战:构建简易脚本示例与技巧详解
    ......
  • 初学Linux笔记
    对linux系统中目录的解释:/bin:bin是Binary的缩写,这个目录存放着最经常使用的命令。/boot:这里存放的是启动Linux时使用的一些核心文件,包括一些连接文件以及镜像文件。/dev:dev是Device(设备)的缩写,存放的是Linux的外部设备,在Linux中访问设备的方式和访问文件的方式是相同的。/e......