韦东山uboot_内核_根文件系统学习笔记5.10-第005课_字符设备驱动_第010节_字符设备驱动程序之异步通知

应用程序:主动去读取硬件状态
①查询方式:非常消耗资源
②中断方式:read函数一直休眠,永远不返回
③poll:指定休眠时间

驱动程序触发应用程序,应用程序再去读取
④异步通知 signal 一旦设备就绪,则主动通知应用程序进行访问。这样,使用无阻塞IO的应用程序无需轮询的查询设备是否可访问,达到减小CPU消耗的目的。类似于硬件上的“中断”的概念,比较准确的称谓是“信号驱动的异步IO”。信号:是在软件层次上对中断机制的模拟。
在原理上,进程接收到一个信号(软件层)<==>(硬件层)处理器接收到一个中断。

kill命令:linux中的kill命令用来终止指定的进程(terminate a process)的运行,是Linux下进程管理的常用命令。通常,终止一个前台进程可以使用Ctrl+C键,但是,对于一个后台进程就须用kill命令来终止,我们就需要先使用ps/pidof/pstree/top等工具获取进程PID,然后使用kill命令来杀掉该进程。kill命令是通过向进程发送指定的信号来结束相应进程的。在默认情况下,采用编号为15的TERM信号。TERM信号将终止所有不能捕获该信号的进程。对于那些可以捕获该信号的进程就要用编号为9的kill信号,强行“杀掉”该进程。
kill -USER1(某个信号) PID(指定进程号)
eg:
1.查看进程号
在这里插入图片描述
2.kill -USER1 833

  1. 目标:按下按键时,驱动程序通知应用程序
  2. 注册信号处理函数?应用程序
  3. 谁发信号?驱动程序发信号
  4. 发给谁?应用程序,应用程序需要告诉驱动它的PID号
  5. 怎么发?驱动程序调用kill_fasyn函数发送

下面看实现代码

为了使设备支持异步通知机制,驱动程序中涉及以下 3 项工作:

  1. 支持 F_SETOWN 命令,能在这个控制命令处理中设置 filp->f_owner 为对应进程 ID。不过此项工作已由内核完成,设备驱动无须处理。
  2. 支持 F_SETFL 命令的处理,每当 FASYNC 标志改变时,驱动程序中的 fasync()函数将得以
    执行。驱动中应该实现 fasync()函数。
  3. 在设备资源可获得时,调用 kill_fasync()函数激发相应的信号

一 驱动程序

  1. 定义结构体:static struct fasync_struct *button_async;
  2. 中断处理函数:
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
	...
	kill_fasync (&button_async, SIGIO, POLL_IN);//发送的信号类型:SIGIO
	...
}

谁来初始化button_async结构体呢?

  1. fifth_drv_fasync函数初始化button_async结构体
static struct file_operations sencod_drv_fops = {
    .owner   =  THIS_MODULE,   
    .open    =  fifth_drv_open,     
	.read	 =	fifth_drv_read,	   
	.release =  fifth_drv_close,
	.poll    =  fifth_drv_poll,
	.fasync	 =  fifth_drv_fasync,			//
};

static int fifth_drv_fasync (int fd, struct file *filp, int on)
{
	printk("driver: fifth_drv_fasync\n");
	return fasync_helper (fd, filp, on, &button_async);//这里初始化button_async结构体
}
  1. fifth_drv_fasync函数什么时候调用?app程序调用驱动的.fasync的时候就会调用。
//把自己的进程号pid告诉驱动程序;
//支持 F_SETOWN 命令,能在这个控制命令处理中设置 filp->f_owner 为对应进程 ID。不过此项工作已由内核完成,设备驱动无须处理。
 app > fcntl(fd,F_SETOWN,pid) 
 //读出F_GETFL
 //支持 F_SETFL 命令的处理,每当 FASYNC 标志改变时,驱动程序中的 fasync()函数将得以执行。驱动中应该实现 fasync()函数。
 app > Oflags = fcntl(fd,F_GETFL)
 app > fcnl(fd,F_SETFL,oflags | FASYNC)

二 应用程序

  1. 声明信号响应函数和响应信号类型
//信号响应函数:my_signal_fun
//相应信号类型:SIGIO
signal(SIGIO, my_signal_fun);
  1. 信号响应函数定义
//从fd中读取按键值到key_val
void my_signal_fun(int signum)
{
	unsigned char key_val;
	read(fd, &key_val, 1);
	printf("key_val: 0x%x\n", key_val);
}

驱动程序的中断服务函数发现有信号则直接给app程序发送信号,app程序跳转到信号处理函数my_signal_fun

//发给谁?
//把自己的进程号pid告诉驱动程序;
//getpid()函数表示获取的进程号
fcntl(fd, F_SETOWN, getpid());

//改变FASYNC标记,最终会调用到驱动的fasync > fasync_helper:初始化/释放fasync_struct
//驱动程序中的 fasync()函数将得以执行
Oflags = fcntl(fd, F_GETFL); 
fcntl(fd, F_SETFL, Oflags | FASYNC);//设置文件描述符标志

三 简单分析fasync_helper函数

调用:fasync_helper (fd, filp, on, &button_async)
作用:对结构体button_async初始化
函数声明原型:

int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
int fd= fd;
struct file * filp = filp;
int on = on;
struct fasync_struct **fapp = &button_async

函数定义:

int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{
	...
	//1. 参数on控制,如果为on分配一个结构体new
	if (on) {
		new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);
		if (!new)
			return -ENOMEM;
	}
	write_lock_irq(&fasync_lock);
	for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
		if (fa->fa_file == filp) {
			if(on) {
				fa->fa_fd = fd;
				kmem_cache_free(fasync_cache, new);
			} else {
				*fp = fa->fa_next;
				kmem_cache_free(fasync_cache, fa);
				result = 1;
			}
			goto out;
		}
	}
	//与1.对应,刚刚分配的结构体new进行初始化后,赋给fapp即&button_async
	if (on) {
		new->magic = FASYNC_MAGIC;
		new->fa_file = filp;
		new->fa_fd = fd;
		new->fa_next = *fapp;
		*fapp = new;
		result = 1;
	}
out:
	write_unlock_irq(&fasync_lock);
	return result;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
韦东山老师为啥要录升级版嵌入式视频?200x年左右,嵌入式Linux在全世界、在中国刚刚兴起。我记得我2005年进入中兴时,全部门的人正在努力学习Linux。在2008年,我写了一本书《嵌入式Linux应用开发完全手册》。它的大概内容是:裸机、U-boot、Linux内核、Linux设备驱动。那时还没有这样讲解整个系统的书,芯片厂家Linux开发包也还不完善,从bootloader到内核,再到设备驱动都不完善。有全系统开发能力的人也很少。于是这书也就恰逢其时,变成了畅销书。我也根据这个思路录制了视频:裸机、U-boot、Linux内核、Linux设备驱动。收获些许名声,带领很多人进入Linux世界。11年过去了,嵌入式Linux世界发生了翻天覆地的变化① 基本系统能用芯片厂家都会提供完整的U-boot、Linux内核、芯片上硬件资源的驱动。方案厂家会做一些定制,比如加上某个WIFI模块,会添加这个WIFI模块的驱动。你可以使用厂家的原始方案,或是使用/借鉴方案商的方案,做出一个“能用”的产品。② 基础驱动弱化;高级驱动专业化基础的驱动,比如GPIO、UART、SPI、I2C、LCD、MMC等,有了太多的书籍、视频、示例代码,修修改改总是可以用的。很多所谓的驱动工程师,实际上就是“调参工程师”。我们群里有名的火哥,提出了一个概念:这些驱动就起一个“hardware enable”的作用。高级的驱动,比如USB、PCIE、HDMI、MIPI、GPU、WIFI、蓝牙、摄像头、声卡。体系非常复杂,很少有人能讲清楚,很多时候只是一笔带过。配置一下应用层工具就了事,能用就成。这些高级驱动,工作中需要专门的人来负责,非常专业。他们是某一块的专家,比如摄像头专家、音频专家。③ 项目为王你到一个公司,目的是把产品做出来,会涉及APP到内核驱动全流程。中小公司玩不起华为中兴的配置,需要的是全面手。大公司里,只负责很小很小一块的镙丝钉,位置也不太稳固啊。所以,如果你不是立志成为某方面的专家,那就做一个全栈工程师吧。④ 调试很重要都说代码是3分写7分调,各种调试调优技术,可以为你的升职加薪加一把火。基于上述4点,我录制的全新视频将有这些特点:1. 快速入门,2. 实战项目,3. 驱动大全,4. 专题,5. 授人以渔,6. 要做任务另外,我们会使用多款芯片同时录制,先讲通用的原理,再单独讲各个板子的操作。这些芯片涵盖主流芯片公司的主流芯片,让你学习工作无缝对接。1.快速入门入门讲究的是快速,入门之后再慢慢深入,特别是对于急着找工作的学生,对于业余时间挑灯夜读的工作了的人,一定要快!再从裸机、U-boot、内核驱动这样的路线学习就不适合了,时间就拉得太长了。搞不好学了后面忘了前面。并且实际工作中并不需要你去弄懂U-boot,会用就行:U-boot比驱动还复杂。讲哪些内容?怎么讲呢?混着讲比如先讲LED APP,知道APP怎么调用驱动,再讲LED硬件原理和裸机,最后讲驱动的编写。这样可以快速掌握嵌入式Linux的整套开发流程,不必像以前那样光学习裸机就花上1、2个月。而里面的裸机程,也会让你在掌握硬件操作的同时,把单片机也学会了。讲基础技能中断、休眠-唤醒、异步通知、阻塞、内存映射等等机制,会配合驱动和APP来讲解。这些技能是嵌入式Linux开发的基础。而这些驱动,只会涉及LED、按制、LCD等几个驱动。掌握了这些输入、输出的驱动和对应的APP后,你已经具备基本的开发能力了。讲配置我们从厂家、从方案公司基本上都可以拿到一套完整的开发环境,怎么去配置它?需要懂shell和python等配置脚本。效果效率优先以前我都是现场写代码、现场写文档,字写得慢,降低了学习效率。这次,效果与效率统一考虑,不再追求所有东西都现场写。容易的地方可先写好代码文档,难的地方现场写。2.实战项目会讲解这样的涉及linux网关/服务器相关项目(不限于,请多提建议):                   定位为:快速掌握项目开发经验,丰满简历。涉及的每一部分都会讲,比如如果涉及蓝牙,在这里只会讲怎么使用,让你能写出程序;如果要深入,可以看后面的蓝牙专题。3. 驱动大全包括基础驱动、高级驱动。这些驱动都是独立成章,深入讲解。虽然基础驱动弱化了,但是作为Linux系统开发人员,这是必备技能,并且从驱动去理解内核是一个好方法。在讲解这些驱动时,会把驱动的运行环境,比如内核调度,进程线程等概念也讲出来,这样就可以搭建一个知识体系。没有这些知识体系的话,对驱动的理解就太肤浅了,等于在Linux框架下写裸机,一叶障目,不见泰山。定位为:工具、字典,用到再学习。4. 专题想深入学习的任何内容,都可独立为专题。比如U-boot专题、内核内存管理专题、systemtap调试专题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值