十一、(正点原子)Linux异步通知

一、简介

        我们首先来回顾一下“中断”,中断是处理器提供的一种异步机制,我们配置好中断以后就可以让处理器去处理其他的事情了,当中断发生以后会触发我们事先设置好的中断服务函数,在中断服务函数中做具体的处理。
        Linux 应用程序可以通过阻塞或者非阻塞这两种方式来访问驱动设备,通过阻塞方式访问的话应用程序会处于休眠态,等待驱动设备可以使用,非阻塞方式的话会通过 poll 函数来不断的轮询,查看驱动设备文件是否可以使用。这两种方式都需要应用程序主动的去查询设备的使用情况。

        “信号”为此应运而生,信号类似于我们硬件上使用的“中断”,只不过信号是软件层次上的。算是在软件层次上对中断的一种模拟,驱动可以通过主动向应用程序发送信号的方式来报告自己可以访问了,应用程序获取到信号以后就可以从驱动设备中读取或者写入数据了。整个过程就相当于应用程序收到了驱动发送过来了的一个中断,然后应用程序去响应这个中断,在整个处理过程中应用程序并没有去查询驱动设备是否可以访问,一切都是由驱动设备自己告诉给应用程序的。

        阻塞、非阻塞、异步通知,这三种是针对不同的场合提出来的不同的解决方法,没有优劣之分,在实际的工作和学习中,根据自己的实际需求选择合适的处理方法即可。

        异步通知的核心就是信号,在 arch/xtensa/include/uapi/asm/signal.h 文件中定义了 Linux 所支持的所有信号,这些信号如下所示:

#define SIGHUP     1 /* 终端挂起或控制进程终止 */
#define SIGINT     2 /* 终端中断(Ctrl+C 组合键) */
#define SIGQUIT    3 /* 终端退出(Ctrl+\组合键) */
#define SIGILL     4 /* 非法指令 */
#define SIGTRAP    5 /* debug 使用,有断点指令产生 */
#define SIGABRT    6 /* 由 abort(3)发出的退出指令 */
#define SIGIOT     6 /* IOT 指令 */
#define SIGBUS     7 /* 总线错误 */
#define SIGFPE     8 /* 浮点运算错误 */
#define SIGKILL    9 /* 杀死、终止进程 */
#define SIGUSR1    10 /* 用户自定义信号 1 */
#define SIGSEGV    11 /* 段违例(无效的内存段) */
#define SIGUSR2    12 /* 用户自定义信号 2 */
#define SIGPIPE    13 /* 向非读管道写入数据 */
#define SIGALRM    14 /* 闹钟 */
#define SIGTERM    15 /* 软件终止 */
#define SIGSTKFLT  16 /* 栈异常 */
#define SIGCHLD    17 /* 子进程结束 */
#define SIGCONT    18 /* 进程继续 */
#define SIGSTOP    19 /* 停止进程的执行,只是暂停 */原子哥在线教学:www.yuanzige.com 论
#define SIGTSTP    20 /* 停止进程的运行(Ctrl+Z 组合键) */
#define SIGTTIN    21 /* 后台进程需要从终端读取数据 */
#define SIGTTOU    22 /* 后台进程需要向终端写数据 */
#define SIGURG     23 /* 有"紧急"数据 */
#define SIGXCPU    24 /* 超过 CPU 资源限制 */
#define SIGXFSZ    25 /* 文件大小超额 */
#define SIGVTALRM  26 /* 虚拟时钟信号 */
#define SIGPROF    27 /* 时钟信号描述 */
#define SIGWINCH   28 /* 窗口大小改变 */
#define SIGIO      29 /* 可以进行输入/输出操作 */
#define SIGPOLL SIGIO
/* #define SIGLOS 29 */
#define SIGPWR     30 /* 断点重启 */
#define SIGSYS     31 /* 非法的系统调用 */
#define SIGUNUSED  31 /* 未使用信号 */

        这些信号中,除了 SIGKILL(9)SIGSTOP(19)这两个信号不能被忽略外,其他的信号都可以忽略。这些信号就相当于中断号,不同的中断号代表了不同的中断,不同的中断所做的处理不同,因此,驱动程序可以通过向应用程序发送不同的信号来实现不同的功能。

        我们使用中断的时候需要设置中断处理函数,同样的,如果要在应用程序中使用信号,那么就必须设置信号所使用的信号处理函数,在应用程序中使用 signal 函数来设置指定信号的处理函数, signal 函数在linux内核中原型如下所示:


#include <signal.h>sighandler_t signal(int signum, sighandler_t handler);

        signum:要设置处理函数的信号。

        handler: 信号的处理函数,原型如下:

typedef void (*sighandler_t)(int);

        返回值: 设置成功的话返回信号的前一个处理函数,设置失败的话返回 SIG_ERR。

        使用kill -9 PID杀死指定进程的方法就是向指定的进程(PID)发送SIGKILL 这个信号。当按下键盘上的 CTRL+C 组合键以后会向当前正在占用终端的应用程序发出 SIGINT 信号, SIGINT 信号默认的动作是关闭当前应用程序。

        这里我们修改一下 SIGINT 信号的默认处理函数,当按下 CTRL+C 组合键以后先在终端上打印出“SIGINT signal!”这行字符串,然后再关闭当前应用程序。

二、驱动中的信号处理

        1、fasync_struct结构体 

        首先我们需要在驱动程序中定义一个fasync_struct结构体指针变量, fasync_struct 结构体定义在linux/fs.h里面内容如下:

struct fasync_struct {spinlock_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;
};

        2、fasync函数 

        如果要使用异步通知,需要在设备驱动中实现 file_operations 操作集中的 fasync 函数,此函数格式如下所示:

int (*fasync) (int fd, struct file *filp, int on);

        fasync 函数里面一般通过调用 fasync_helper 函数来初始化前面定义的 fasync_struct 结构体指针, fasync_helper 函数定义在linux/fs.h里面,原型如下:

extern int fasync_helper(int fd, struct file *filp, int on, struct fasync_struct **fapp);

        fasync_helper 函数的前三个参数就是 fasync 函数的那三个参数,第四个参数就是要初始化的 fasync_struct 结构体指针变量。当应用程序通过“fcntl(fd, F_SETFL, flags | FASYNC)”改变fasync 标记的时候,驱动程序 file_operations 操作集中的 fasync 函数就会执行。

        驱动程序中的 fasync 函数参考示例如下:

struct xxx_dev {struct fasync_struct *async_queue; /* 异步相关结构体 */
};static int xxx_fasync(int fd, struct file *filp, int on)
{struct xxx_dev *dev = (xxx_dev)filp->private_data;    /* 获取私有数据 */if (fasync_helper(fd, filp, on, &dev->async_queue) < 0)return -EIO;return 0;
}static struct file_operations xxx_ops = {.......fasync = xxx_fasync,......
};

        在关闭驱动文件的时候需要在file_operations操作集中的release函数中释放fasync_structfasync_struct的释放函数同样为fasync函数,release函数参数参考实例如下:

static int xxx_release(struct inode *inode, struct file *filp)
{return xxx_fasync(-1, filp, 0); /* 删除异步通知 */
}

        3、kill_fasync函数

        当设备可以访问的时候,驱动程序需要向应用程序发出信号,相当于产生“中断”。 kill_fasync函数负责发送指定的信号, kill_fasync 函数定义在linux/fs.h里面原型如下所示:

extern void kill_fasync(struct fasync_struct **fpps, int sig, int band);

        fpps: 要操作的 fasync_struct异步通知结构体。

        sig:要发送的信号。

        band:可读时设置为POLL_ON,可写时设置为POLL_OUT

三、应用程序对异步通知的处理

        1、注册信号处理函数

        应用程序根据驱动程序所使用的信号来设置信号的处理函数,应用程序使用 signal 函数来设置信号的处理函数。

        2、将本应用程序的进程号告诉内核

        使用 fcntl(fd, F_SETOWN, getpid())将本应用程序的进程号告诉给内核

        3、开启异步通知

        使用如下两行程序开启异步通知:

flags = fcntl(fd, F_GETFL);          /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC);  /* 开启当前进程异步通知功能 */

        重点就是通过 fcntl 函数设置进程状态为 FASYNC,经过这一步,驱动程序中的 fasync 函数就会执行。


四、总结

        异步通知就是用户空间想对驱动空间读或者写某个数据时,不需要一直去查看此数据是否可读或可写,可以去执行其他的任务,当要读或写的数据可以被读或写的时候,驱动空间会向用户空间发送一个信号。当空户空间接收到这个信号时,就停止现在的任务去执行读或写数据。

        驱动空间怎么向用户空间发送这个信号呢?这里就引出了在驱动空间中文件操作集合file_operations,中的fasync函数了,这个函数就是驱动空间自己查询用户空间想读写的数据的函数,当用户空间调用函数fcntl并且设置参数为fcntl(fd, F_SETFL, flags | FASYNC),意思是开启驱动空间的异步通知功能。这样,驱动空间的fasync函数就会一直调用查询,当查询到数据可以读或写时,在fasync里面使用fasync_helper函数就可以实现将信号传递给用户空间。

        当用户空间收到驱动空间的型号后,如果用户空间有多个进程,那么这个信号时传递给谁使用呢?所以,我们在用户空间中开启异步通知后,需要将开启异步通知的进程的进程号告诉给Linux内核,这样,当驱动空间传递信号过来,用户空间就知道这个信号给谁使用了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/872212.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM常见知识点总结

最近在看关于JVM的一些知识点&#xff0c;之前也有了解过&#xff0c;可是时间一长就总会忘&#xff0c;索性按照自己的记忆方式去回顾一下知识点。 首先&#xff0c;jvm是Java程序运行的环境&#xff0c;全称为Java Virtual Machine 当我们写好的Java文件经过javac编译后就变成…

Java SE—基本数据类型(详细讲解)

&#x1f4dd;个人主页&#x1f339;&#xff1a;誓则盟约 ⏩收录专栏⏪&#xff1a;Java SE &#x1f921;往期回顾&#x1f921;&#xff1a;Python 神器&#xff1a;wxauto 库——解锁微信自动化的无限可能 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f3…

售前工程师的AI大模型转型之路

随着人工智能技术的飞速发展&#xff0c;大模型&#xff08;Large Language Models, LLMs&#xff09;正逐渐成为IT行业的热点。对于售前工程师来说&#xff0c;转行大模型领域不仅意味着新的机遇&#xff0c;也面临着诸多挑战。本文将探讨售前工程师转行大模型领域的机遇与挑战…

嵌入式人工智能(6-树莓派4B按键输入控制LED)

1、按键 按键的原理都是一样&#xff0c;通过按键开关的按下导通&#xff0c;抬起断开的情况&#xff0c;GPIO引脚来检测其是否有电流流入。GPIO有input()方法&#xff0c;对于GPIO引脚检测电流&#xff0c;不能让其引脚悬空&#xff0c;否则引脚会受周边环境电磁干扰产生微弱…

SpringBoot之健康监控(Actuator)

1&#xff0c;基本介绍 Spring Actuator 是 Spring Boot 提供的一个扩展模块&#xff0c;用于监控和管理应用程序的生产环境。它通过 HTTP 端点暴露了大量的监控和管理功能&#xff0c;使得开发者可以在运行时查看应用程序的运行状况、配置信息、性能指标等。 主要功能&#…

探索未来视界:LC201小尺寸红外热成像机芯引领行业新变革

在当今科技飞速发展的时代&#xff0c;红外热成像技术正以前所未有的方式革新着诸多行业&#xff0c;而灵卡技术推出的LC201小尺寸红外热成像机芯&#xff0c;正是这一变革的先锋。专为体积和功耗敏感领域设计&#xff0c;LC201以超紧凑的22x22mm尺寸、轻量化设计&#xff0c;及…

什么是边缘计算技术和边缘计算平台?

随着物联网、5G技术和人工智能的不断发展&#xff0c;数据的规模和种类也在快速增加。在这种背景下&#xff0c;传统的云计算模式面临着一些问题&#xff0c;例如延迟高、网络拥塞等&#xff0c;这些问题限制了数据的处理速度和效率&#xff0c;降低了用户的使用体验。为了解决…

非对称加解密和签名

非对称加解密特点 一对密钥&#xff0c;公钥和私钥。私钥自己持有&#xff0c;公钥公开给通信的对方。 如果公钥用作加密&#xff0c;那么私钥用作解密。 如果私钥用做加密&#xff0c;那么公钥用作解密。 如果数据量少可用非对称加密算法直接加密。 如果数据量大&#xff0c;…

【Python爬虫教程】第7篇-requests模块的cookies保存和使用

文章目录 为什么要保存cookiesrequests.utils工具类保存cookies到本地文件从本地文件解析cookies使用使用实践 为什么要保存cookies 保存cookies是避免每次都登录获取权限&#xff0c;一遍权限是有过期时间的&#xff0c;不需要每次重复登录&#xff0c;可以将cookies保存起来…

MySQL事务隔离级别+共享锁,排他锁,乐观锁,悲观锁

在操作数据库的时候&#xff0c;可能会由于并发问题而引起的数据的不一致性&#xff08;数据冲突&#xff09;。 MySQL事务隔离级别 一个事务的执行&#xff0c;本质上就是一条工作线程在执行&#xff0c;当出现多个事务同时执行时&#xff0c;这种情况则被称之为并发事务&am…

AI算法16-贝叶斯线性回归算法Bayesian Linear Regression | BLR

贝叶斯线性回归算法简介 频率主义线性回归概述 线性回归的频率主义观点可能你已经学过了&#xff1a;该模型假定因变量&#xff08;y&#xff09;是权重乘以一组自变量&#xff08;x&#xff09;的线性组合。完整的公式还包含一个误差项以解释随机采样噪声。如有两个自变量时…

excel系列(二) - 利用 easypoi 快速实现 excel 文件导入导出

一、介绍 在上篇文章中&#xff0c;我们介绍了 apache poi 工具实现 excel 文件的导入导出。 本篇我们继续深入介绍另一款优秀的 excel 工具库&#xff1a;easypoi。 二、easypoi 以前的以前&#xff0c;有个大佬程序员&#xff0c;跳到一家公司之后就和业务人员聊上了&…

Parallels Desktop 19 for Mac(PD19虚拟机)详细图文安装教程分享

Parallels Desktop 19是一款功能丰富、性能强大且易于使用的虚拟机软件&#xff0c;它可以让您在Mac上同时运行多个操作系统&#xff0c;为您提供更大的灵活性和兼容性。 Parallels Desktop 19 for Mac(PD19虚拟机)下载安装包 Parallels Desktop 19 for Mac(PD19虚拟机)详细图…

linux端口,进程管理,主机状态监控

linux端口&#xff0c;进程管理&#xff0c;主机状态监控 一、端口 1、什么是端口?2、端口的划分2、查看端口占用 二、进程 1、什么是进程2、查看进程信息2、关闭进程 三、主机状态监控 1、查看资源占用2、磁盘信息监控3、查看网络情况 四、命令总结 一、端口 1、什么是端口…

电力调度台如何助力电力指挥中心更智慧

在现代电力系统的复杂运行环境中&#xff0c;电力调度台正逐渐成为电力指挥中心实现智慧化管理的关键力量。 电力调度台具备强大的信息集成与处理能力。它能够将来自不同监测系统、传感器和数据源的海量数据汇聚一处&#xff0c;包括电力设备的实时运行状态、电力负荷的动态变化…

C++客户端Qt开发——常用控件(输入类控件)

4.输入类控件 ①Line Edit 单行输入框 属性 说明 text 输入框中的文本 input 输入内容格式约束 maxLength 最大长度 frame 是否添加边框 echoMode 显示方式 QLineEdit::Normal&#xff1a;这是默认值&#xff0c;文本框会显示输入的文本。 QLineEdit::Password&…

C++客户端Qt开发——常用控件(多元素控件)

5.多元素控件 Qt中提供的多元素控件有&#xff1a; QListWidget QListView QTablewidget QTableview QTreewidget QTreeview xxWidget和xView之间的区别 以QTableWidget和QTableView为例. QTableView是基于MVC设计的控件.QTableView自身不持有数据.使用QTableView的时候需要…

Django任务管理

1、用django-admin命令创建一个Django项目 django-admin startproject task_manager 2、进入到项目下用命令创建一个应用 cd task_manager python manage.py startapp tasks 3、进入models.py定义数学模型 第2步得到的只是应用的必要空文件&#xff0c;要开始增加各文件实际…

STM32(五):STM32指南者-按键控制灯开关实验

说明&#xff1a;源代码和教程可从野火处下载&#xff0c;本博客为了记录学习过程STM32&#xff08;四&#xff09;&#xff1a;STM32指南者-跑马灯实验的基础上 一、采用轮询方式1、bsp_key.h2、bsp_key.c3、main.c 二、采用中断方式1、bsp_exti.h2、bsp_exti.c3、stm32f10x_i…

AI写作不懂提示词 大象Prompt 保姆级系列教程三

一、提示词的核心价值究竟是啥&#xff1f; 最近跟不少业内朋友探讨这事儿&#xff0c;我觉得&#xff1a;提示词的核心价值在于对方法论的封装以及由此带来的知识传播速度加快。 通俗讲&#xff0c;假如你熟悉的行业里有个厉害的“老师傅”&#xff0c;他在核心业务上有好多心…