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

嵌入式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.rhkb.cn/news/146969.html

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

相关文章

【算法分析与设计】回溯法(上)

目录 一、学习要点1.1 回溯法1.2 问题的解空间1.3 0-1背包问题的解空间1.4 旅行售货员问题的解空间1.5 生成问题状态的基本方法 二、回溯法的基本思想三、回溯算法的适用条件四、递归回溯五、迭代回溯六、子集树与排列树七、装载问题八、批处理作业调度问题 一、学习要点 理解回…

【数据结构与算法】通过双向链表和HashMap实现LRU缓存 详解

这个双向链表采用的是有伪头节点和伪尾节点的 与上一篇文章中单链表的实现不同,区别于在实例化这个链表时就初始化了的伪头节点和伪尾节点,并相互指向,在第一次添加节点时,不需要再考虑空指针指向问题了。 /*** 通过链表与HashMa…

92、Redis ------- 使用 Lettuce 操作 Redis 的方法和步骤----(文字讲解无代码)

lettuce :英语的意思:生菜 是一个用来操作redis的框架,springboot内置默认支持的也是 lettuce ,也可以自己改成 jedis Jedis 也是一个用来操作redis的框架 ★ Lettuce的核心API RedisURI:用于封装Redis服务器的URI信息…

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

嵌入式Linux应用开发-基础知识-第十九章驱动程序基石⑤ 第十九章 驱动程序基石⑤19.9 mmap19.9.1 内存映射现象与数据结构19.9.2 ARM架构内存映射简介19.9.2.1 一级页表映射过程19.9.2.2 二级页表映射过程 19.9.3 怎么给APP新建一块内存映射19.9.3.1 mmap调用过程19.9.3.2 cach…

数据结构:简单记录顺序表、链表、栈、队列

初学者很容易认为顺序表、链表、栈、队列是四种并列的数据结构,其实仔细想想并不是。 注意区分: 顺序表和链表是指数据的存储结构,是线性表的一种,顺序表一般指的就是数组,数据存储的逻辑顺序和物理顺序都是连续的&a…

nodejs+vue交通违章查询及缴费elementui

第三章 系统分析 10 3.1需求分析 10 3.2可行性分析 10 3.2.1技术可行性:技术背景 10 3.2.2经济可行性 11 3.2.3操作可行性: 11 3.3性能分析 11 3.4系统操作流程 12 3.4.1管理员登录流程 12 3.4.2信息添加流程 12 3.4.3信息删除流程 13 第四章 系统设计与…

Django模板加载与响应

前言 Django 的模板系统将 Python 代码与 HTML 代码解耦,动态地生成 HTML 页面。Django 项目可以配置一个或多个模板引擎,但是通常使用 Django 的模板系统时,应该首先考虑其内置的后端 DTL(Django Template Language,D…

Git使用【下】

欢迎来到Cefler的博客😁 🕌博客主页:那个传说中的man的主页 🏠个人专栏:题目解析 🌎推荐文章:题目大解析(3) 目录 👉🏻标签管理理解标签标签运用 …

Grander因果检验(格兰杰)原理+操作+解释

笔记来源: 1.【传送门】 2.【传送门】 前沿原理介绍 Grander因果检验是一种分析时间序列数据因果关系的方法。 基本思想在于,在控制Y的滞后项 (过去值) 的情况下,如果X的滞后项仍然有助于解释Y的当期值的变动,则认为 X对 Y产生…

插入排序与希尔排序

个人主页:Lei宝啊 愿所有美好如期而遇 前言: 这两个排序在思路上有些相似,所以有人觉得插入排序和希尔排序差别不大,事实上,他们之间的差别不小,插入排序只是希尔排序的最后一步。 目录 前言:…

华为数通方向HCIP-DataCom H12-831题库(单选题:161-180)

第161题 某台路由器Router LSA如图所示,下列说法中错误的是? A、本路由器已建立邻接关系 B、本路由器为DR C、本路由支持外部路由引入 D、本路由器的Router ID为10.0.12.1 答案: B 解析: 一类LSA的在transnet网络中link id值为DR的route id ,但Link id的地址不是10.0.12.…

对pyside6中的textedit进行自定义,实现按回车可以触发事件。

以下方法不算最优解。因为这个ui文件很容易重新编译,使写在ui.py里面的代码被删掉。 所以更好的方法应该是在主代码当中单独定义控件。并且使用布局添加控件到界面中。 以下内容纯为旧版实现,仅供参考: 我的实现方法是,先用qt de…

学信息系统项目管理师第4版系列15_资源管理基础

1. 项目资源 1.1. 实物资源 1.1.1. 着眼于以有效和高效的方式,分配和使用完成项目所需的实物资源 1.1.2. 包括设备、材料、设施和基础设施 1.2. 团队资源 1.2.1. 人力资源 1.2.2. 包含了技能和能力要求 2. 人力资源管理 2.1. 不仅是组织中最重要的资源之一&…

设计模式之抽象工厂模式--创建一系列相关对象的艺术(简单工厂、工厂方法、到抽象工厂的进化过程,类图NS图)

目录 概述概念适用场景结构类图 衍化过程业务需求基本的数据访问程序工厂方法实现数据访问程序抽象工厂实现数据访问程序简单工厂改进抽象工厂使用反射抽象工厂反射配置文件衍化过程总结 常见问题总结 概述 概念 抽象工厂模式是一种创建型设计模式,它提供了一种将相…

【开发篇】十、Spring缓存:手机验证码的生成与校验

文章目录 1、缓存2、用HashMap模拟自定义缓存3、SpringBoot提供缓存的使用4、手机验证码案例完善 1、缓存 缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高…

Shapiro-Francia正态检验

Shapiro-Francia检验是一种用于检验数据是否来自正态分布的统计方法。它是Shapiro-Wilk检验的一个变种,通常适用于小到中等样本大小的数据集。Shapiro-Francia检验的核心思想是通过计算统计量来评估数据的正态性。 Shapiro-Francia检验的零假设是数据来自正态分布&…

26 docker前后端部署

[参考博客]((257条消息) DockerNginx部署前后端分离项目(SpringBootVue)的详细教程_在docker中安装nginx实现前后端分离_这里是杨杨吖的博客-CSDN博客) (DockerNginx部署前后端分离项目(SpringBootVue)) 安装docker # 1、yum 包更新到最新 yum update # 2、安装需要的软件包…

JavaSE | 初识Java(七) | 数组 (下)

Java 中提供了 java.util.Arrays 包 , 其中包含了一些操作数组的常用方法 代码实例: import java.util.Arrays int[] arr {1,2,3,4,5,6}; String newArr Arrays.toString(arr); System.out.println(newArr); // 执行结果 [1, 2, 3, 4, 5, 6] 数组拷贝 代码实例…

cf 解题报告 01

E. Power of Points Problem - 1857E - Codeforces 题意: 给你 n n n 个点,其整数坐标为 x 1 , … x n x_1,\dots x_n x1​,…xn​,它们位于一条数线上。 对于某个整数 s s s,我们构建线段[ s , x 1 s,x_1 s,x1​], [ s , x…

C语言结构体指针学习

结构体变量存放内存中,也有起始地址,定义一个变量来存放这个地址,那这个变量就是结构体指针; typedef struct mydata{int a1;int a2;int a3; }mydata;void CJgtzzView::OnDraw(CDC* pDC) {CJgtzzDoc* pDoc GetDocument();ASSERT…