嵌入式Linux应用开发-基础知识-第十九章驱动程序基石②

news2024/11/24 19:11:48

嵌入式Linux应用开发-基础知识-第十九章驱动程序基石②

  • 第十九章 驱动程序基石②
    • 19.3 异步通知
      • 19.3.1 适用场景
      • 19.3.2 使用流程
      • 19.3.3 驱动编程
      • 19.3.4 应用编程
      • 19.3.5 现场编程
      • 19.3.6 上机编程
      • 19.3.7 异步通知机制内核代码详解
    • 19.4 阻塞与非阻塞
      • 19.4.1 应用编程
      • 19.4.2 驱动编程
      • 19.4.3 驱动开发原则

第十九章 驱动程序基石②

在这里插入图片描述

19.3 异步通知

使用 GIT命令载后,本节源码位于这个目录下:

01_all_series_quickstart\ 
05_嵌入式 Linux驱动开发基础知识\source\ 
06_gpio_irq\ 
    05_read_key_irq_poll_fasync 

19.3.1 适用场景

在前面引入中断时,我们曾经举过一个例子:
在这里插入图片描述

妈妈怎么知道卧室里小孩醒了?
① 时不时进房间看一下:查询方式
简单,但是累
② 进去房间陪小孩一起睡觉,小孩醒了会吵醒她:休眠-唤醒
不累,但是妈妈干不了活了
③ 妈妈要干很多活,但是可以陪小孩睡一会,定个闹钟:poll方式 要浪费点时间,但是可以继续干活。
妈妈要么是被小孩吵醒,要么是被闹钟吵醒。
④ 妈妈在客厅干活,小孩醒了他会自己走出房门告诉妈妈:异步通知 妈妈、小孩互不耽误
使用休眠-唤醒、POLL机制时,都需要休眠等待某个事件发生时,它们的差别在于后者可以指定休眠的时长。
在现实生活中:妈妈可以不陪小孩睡觉,小孩醒了之后可以主动通知妈妈。
如果 APP不想休眠怎么办?也有类似的方法:驱动程序有数据时主动通知APP,APP收到信号后执行信息处理函数。
什么叫“异步通知”?
你去买奶茶:
你在旁边等着,眼睛盯着店员,生怕别人插队,他一做好你就知道:你是主动等待他做好,这叫“同步”。 你付钱后就去玩手机了,店员做好后他会打电话告诉你:你是被动获得结果,这叫“异步”。

19.3.2 使用流程

驱动程序怎么通知 APP:发信号,这只有 3个字,却可以引发很多问题:
① 谁发:驱动程序发
② 发什么:信号
③ 发什么信号:SIGIO
④ 怎么发:内核里提供有函数
⑤ 发给谁:APP,APP要把自己告诉驱动
⑥ APP收到后做什么:执行信号处理函数
⑦ 信号处理函数和信号,之间怎么挂钩:APP注册信号处理函数
小孩通知妈妈的事情有很多:饿了、渴了、想找人玩。
Linux系统中也有很多信号,在 Linux内核源文件 include\uapi\asm-generic\signal.h中,有很多信号的宏定义:
在这里插入图片描述

就 APP而言,你想处理 SIGIO信息,那么需要提供信号处理函数,并且要跟 SIGIO挂钩。这可以通过一个 signal函数来“给某个信号注册处理函数”,用法如下:
在这里插入图片描述

APP还要做什么事?想想这几个问题:
① 内核里有那么多驱动,你想让哪一个驱动给你发 SIGIO信号?
APP要打开驱动程序的设备节点。
② 驱动程序怎么知道要发信号给你而不是别人?
APP要把自己的进程 ID告诉驱动程序。
③ APP有时候想收到信号,有时候又不想收到信号:
应该可以把 APP的意愿告诉驱动。
驱动程序要做什么?发信号。
① APP设置进程 ID时,驱动程序要记录下进程 ID;
② APP还要使能驱动程序的异步通知功能,驱动中有对应的函数:
APP打开驱动程序时,内核会创建对应的 file结构体,file中有 f_flags; f_flags中有一个 FASYNC位,它被设置为 1时表示使能异步通知功能。
当 f_flags中的 FASYNC位发生变化时,驱动程序的 fasync函数被调用。
③ 发生中断时,有数据时,驱动程序调用内核辅助函数发信号。
这个辅助函数名为 kill_fasync。
完美!
APP收到信号后,是怎么执行信号处理函数的?
这个,很难,有兴趣的话就看本节最后的文档。初学者没必要看。
综上所述,使用异步通知,也就是使用信号的流程如下图所示:
在这里插入图片描述

重点从②开始:
② APP给 SIGIO这个信号注册信号处理函数 func,以后 APP收到 SIGIO信号时,这个函数会被自动调用;
③ 把 APP的 PID(进程 ID)告诉驱动程序,这个调用不涉及驱动程序,在内核的文件系统层次记录 PID; ④ 读取驱动程序文件 Flag;
⑤ 设置 Flag里面的 FASYNC位为 1:当 FASYNC位发生变化时,会导致驱动程序的 fasync被调用;
⑥⑦ 调用 faync_helper,它会根据 FAYSNC的值决定是否设置 button_async->fa_file=驱动文件filp:
驱动文件 filp结构体里面含有之前设置的 PID。
⑧ APP可以做其他事;
⑨⑩ 按下按键,发生中断,驱动程序的中断服务程序被调用,里面调用 kill_fasync发信号;
⑪⑫⑬ APP收到信号后,它的信号处理函数被自动调用,可以在里面调用 read函数读取按键。

19.3.3 驱动编程

使用异步通知时,驱动程序的核心有 2:

① 提供对应的 drv_fasync函数; ② 并在合适的时机发信号。
drv_fasync函数很简单,调用 fasync_helper函数就可以,如下:

static struct fasync_struct *button_async; 
static int drv_fasync (int fd, struct file *filp, int on) 
{ 
 return fasync_helper (fd, filp, on, &button_async); 
} 

fasync_helper函数会分配、构造一个 fasync_struct结构体 button_async:
① 驱动文件的 flag被设置为 FAYNC时:

button_async->fa_file = filp;  // filp表示驱动程序文件,里面含有之前设置的 PID ② 驱动文件被设置为非 FASYNC时: 
button_async->fa_file = NULL; 

以后想发送信号时,使用 button_async作为参数就可以,它里面“可能”含有 PID。
什么时候发信号呢?在本例中,在 GPIO中断服务程序中发信号。
怎么发信号呢?代码如下:

kill_fasync (&button_async, SIGIO, POLL_IN); 

第 1个参数:button_async->fa_file非空时,可以从中得到 PID,表示发给哪一个 APP; 第 2个参数表示发什么信号:SIGIO;
第 3个参数表示为什么发信号:POLL_IN,有数据可以读了。(APP用不到这个参数)

19.3.4 应用编程

应用程序要做的事情有这几件:
① 编写信号处理函数:

static void sig_func(int sig) 
{ 
 int val; 
 read(fd, &val, 4); 
 printf("get button : 0x%x\n", val); 
 } 

② 注册信号处理函数:

signal(SIGIO, sig_func); 

③ 打开驱动:

fd = open(argv[1], O_RDWR); 

④ 把进程 ID告诉驱动:

fcntl(fd, F_SETOWN, getpid()); 

⑤ 使能驱动的 FASYNC功能:

flags = fcntl(fd, F_GETFL); 
fcntl(fd, F_SETFL, flags | FASYNC); 

19.3.5 现场编程

19.3.6 上机编程

19.3.7 异步通知机制内核代码详解

还没写

19.4 阻塞与非阻塞

所谓阻塞,就是等待某件事情发生。比如调用 read读取按键时,如果没有按键数据则 read函数不会返回,它会让线程休眠等待。
使用 poll时,如果传入的超时时间不为 0,这种访问方法也是阻塞的。

使用 poll时,可以设置超时时间为 0,这样即使没有数据它也会立刻返回,这就是非阻塞方式。能不能让 read函数既能工作于阻塞方式,也可以工作于非阻塞方式?可以!
APP调用 open函数时,传入 O_NONBLOCK,就表示要使用非阻塞方式;默认是阻塞方式。
注意:对于普通文件、块设备文件,O_NONBLOCK不起作用。
注意:对于字符设备文件,O_NONBLOCK起作用的前提是驱动程序针对 O_NONBLOCK做了处理。
只能在 open时表明 O_NONBLOCK吗?在 open之后,也可以通过 fcntl修改为阻塞或非阻塞。
使用 GIT命令载后,本节源码位于这个目录下:

01_all_series_quickstart\ 
05_嵌入式 Linux驱动开发基础知识\source\ 
06_gpio_irq\ 
    06_read_key_irq_poll_fasync_block 

19.4.1 应用编程

open时设置:

int  fd = open(/dev/xxx”, O_RDWR | O_NONBLOCK);  /* 非阻塞方式 */ int  fd = open(/dev/xxx”, O_RDWR );  /* 阻塞方式 */ 

open之后设置:

int flags = fcntl(fd, F_GETFL); 
fcntl(fd, F_SETFL, flags | O_NONBLOCK);  /* 非阻塞方式 */ 
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);  /* 阻塞方式 */ 

19.4.2 驱动编程

以 drv_read为例:

static ssize_t drv_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)if (queue_empty(&as->queue) && fp->f_flags & O_NONBLOCK) 
  return -EAGAIN; 
 
wait_event_interruptible(apm_waitqueue, !queue_empty(&as->queue)); 
    …… 
} 

从驱动代码也可以看出来,当 APP打开某个驱动时,在内核中会有一个 struct file结构体对应这个驱动,这个结构体中有 f_flags,就是打开文件时的标记位;可以设置 f_flasgs的 O_NONBLOCK位,表示非阻塞;也可以清除这个位表示阻塞。
驱动程序要根据这个标记位决定事件未就绪时是休眠和还是立刻返回。

19.4.3 驱动开发原则

驱动程序程序“只提供功能,不提供策略”。就是说驱动程序可以提供休眠唤醒、查询等等各种方式,,驱动程序只提供这些能力,怎么用由 APP决定。

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

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

相关文章

26-网络通信

网络通信 什么是网络编程? 可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)。 java.net.包下提供了网络编程的解决方案! 基本的通信架构有2种形式:CS架构( Client客户端/Server服…

Python无废话-办公自动化Excel修改数据

如何修改Excel 符合条件的数据?用Python 几行代码搞定。 需求:将销售明细表的产品名称为PG手机、HW手机、HW电脑的零售价格分别修改为4500、5500、7500,并保存Excel文件。如下图 Python 修改Excel 数据,常见步骤: 1&…

又添十万字-CS的陋室2023年文章合集来袭

趁着国庆中秋双节,整理了“22年文章合集”以来的所有新文章,在此给大家带来我的文章合集2023版。 文章合集收录: 文章合集2022以来的所有文章,包括“前沿重器”和“心法利器”。 前沿重器28-34共7篇,约2.6万字。心法利…

《C和指针》笔记30:函数声明数组参数、数组初始化方式和字符数组的初始化

文章目录 1. 函数声明数组参数2. 数组初始化方式2.1 静态初始化2.2 自动变量初始化 2.2 字符数组的初始化 1. 函数声明数组参数 下面两个函数原型是一样的: int strlen( char *string ); int strlen( char string[] );可以使用任何一种声明,但哪个“更…

一文拿捏SpringMVC的调用流程

SpringMVC的调用流程 1.核心元素: DispatcherServlet(前端控制器)HandlerMapping(处理器映射器)HandlerAdapter(处理器适配器) ---> Handler(处理器)ViewResolver(视图解析器 )---> view(视图) 2.调用流程 用户发送请求到前端控制器前端控制器接收用户请求…

7.JavaScript-vue

1 JavaScript html完成了架子,css做了美化,但是网页是死的,我们需要给他注入灵魂,所以接下来我们需要学习JavaScript,这门语言会让我们的页面能够和用户进行交互。 1.1 介绍 通过代码/js效果演示提供资料进行效果演…

<C++> STL_bitset使用和模拟实现

bitset的介绍 位图的引入 给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中? 要判断一个数是否在某一堆数中,我们可能会想到如下方法: 将这一堆数进行排序&#xff0…

机器学习必修课 - 如何处理缺失数据

运行环境:Google Colab 处理缺失数据可简单分为两种方法:1. 删除具有缺失值的列 2. 填充 !git clone https://github.com/JeffereyWu/Housing-prices-data.git下载数据集 import pandas as pd from sklearn.model_selection import train_test_split导…

EM@常用三角函数图象性质(中学部分)

文章目录 abstract正弦函数正弦型函数转动相关概念旋转角速度转动周期转动频率初相小结 余弦函数的图象与性质性质 正切函数的图象和性质由已知三角函数值求角任意角范围内反三角函数(限定范围内)反正弦反余弦反正切 abstract 讨论 sin ⁡ , cos ⁡ , tan ⁡ \sin,\cos,\tan s…

WEB 3D 技术,通过node环境创建一个three案例

好 文章 前端3D Three.js 在本地搭建一个官方网站 中我们 搭建了一个Three的官网 现在呢 我们就来创建第一个ThreeJs的资源 这里呢 我们还是选择一个脚手架的开发模式 因为现在基本所有的前端都在使用这样的开发方式 这里 我们创建一个文件夹目录 作为我们项目的存放目录 我们…

【MySQL教程】| (1-1) 2023MySQL-8.1.0 安装教程

文章目录 一、安装包下载二、安装配置1、解压安装包2、编写MySQL配置文件3、初始化MySQL数据库3、安装mysql服务并启动4、MySQL服务5、连接MySQL6、修改密码 三、配置环境变量四、防止mysql自启动拖慢开机时间 近日有粉丝问到mysql在win11的安装中遇到一些问题,应粉…

基于 QT 实现 Task Timer,高效利用时间

一、开发环境 Ubuntu 20.04 QT6.0 二、新建 Qt Wigets Application 这里的基类选择 Wigets, pro 配置文件添加 sql 模块,需要用到 sqlite, QT sql 三、添加数据库连接头文件 // connection.h #ifndef CONNECTION_H #define CONNECTION_…

pycharm配置python3.8版本专门用于undecteded_chromedriver测试

pycharm配置python3.8版本专门用于undecteded_chromedriver测试 作者:虚坏叔叔 博客:https://pay.xuhss.com 早餐店不会开到晚上,想吃的人早就来了!😄 一、Pycharm及python环境的配置 1.安装python-3.8.7rc1-amd64.e…

QT常用控件介绍

QT信号与槽机制 connect (A,SIGNLA(aaa()),B, SLOT(bbb())); GUI继承简介 布局管理器 垂直布局水平布局网格布局表单布局 输出控件 Label: 标签Text Browser: 文本浏览器Graphics View : 图形视图框架Calendar Widget: 日历控件LCD Number: 液晶字体数…

游戏逆向中的 NoClip 手段和安全应对方式

文章目录 墙壁边界寻找碰撞 NoClip 是一种典型的黑客行为,允许你穿过墙壁,所以 NoClip 又可以认为是避免碰撞体积的行为 墙壁边界 游戏中设置了碰撞体作为墙壁边界,是 玩家对象 和墙壁发生了碰撞,而不是 相机 玩家对象有他的 X…

操作系统初探 - 进程的概念

目录 预备知识 冯诺依曼和现代计算机结构 操作系统的理解 进程和PCB的概念 PCB中的信息 查看进程信息的指令 - ps pid 进程状态 预备知识 在学习操作系统之前我们需要先了解一下如下的预备知识。 冯诺依曼和现代计算机结构 美籍匈牙利科学家冯诺依曼最先提出“程序存…

【CAD二次开发】给CAD添加TRUSTEDPATHS避免dll插件信任弹窗

找到配置文件目录,遍历下面的每个配置文件; 找到 Variables 下的TRUSTEDPATHS项目;在后面添加新的目录即可,多个目录使用分号分隔; public static void AddPath(string trusedPath){// 指定注册表键的路径

画CMB天图使用Planck配色方案

使用Planck的配色方案: 全天图: 或者方形图: 使用下面设置即可: import pspy, pixell from pspy.so_config import DEFAULT_DATA_DIR pixell.colorize.mpl_setdefault("planck")此方法不会改变matplotlib默认配色方案…

浏览器指定DNS

edge--设置 https://dns.alidns.com/dns-query

JavaSE | 初识Java(六) | 数组 (上)

数组的创建及初始化 T[] 数组名 new T[N]; //T:表示数组中存放元素的类型 //T[]:表示数组的类型 //N:表示数组的长度 int[] array1 new int[10]; // 创建一个可以容纳10个int类型元素的数组 double[] array2 new double[5]; // 创建一个可…