【i.MX6ULL】驱动开发10——阻塞&非阻塞式按键检测

上篇文章:介绍了linux中的五种I/O模型,本篇,就来使用阻塞式I/O和非用阻塞式I/O 是否降低。
1 阻塞I/O方式的按键检测
1.1 阻塞I/O之等待队列
阻塞访问最大的好处就是当设备文件不可操作的时候进程可以进入休眠态,这样可以将CPU资源让出来。但是,当设备文件可以操作的时候就必须唤醒进程,一般在中断函数里面完成唤醒工作。Linux 内核提供了等待队列(wait queue)来实现阻塞进程的唤醒工作。
等待队列头使用结构体wait_queue_head_t 表示:
使用 init_waitqueue_head 函数初始化等待队列头:
当设备不可用的时, 将这些进程对应的等待队列项(wait_queue_t )添加到等待队列里面:
使用宏 DECLARE_WAITQUEUE 定义并初始化一个等待队列项:
DECLARE_WAITQUEUE(name, tsk)
当设备不可访问的时候就需要将进程对应的等待队列项添加到前面创建的等待队列头中:
当设备可以访问以后再将进程对应的等待队列项从等待队列头中删除即可:
当设备可以使用的时候就要唤醒进入休眠态的进程:
1.2 阻塞I/O程序编写
这里仅介绍与之前按键程序的主要区别。
1.2.1驱动程序
阻塞读取逻辑如下,首先要定义一个等待队列,当按键没有按下时,就要阻塞等待了(将等待队列添加到等待队列头),然后进行行一次任务切换,交出CPU的使用权。等待有按键按下时,会有信号唤醒该等待,并将按键值返回给应用层的程序。
按键的定时器去抖逻辑中的,读取到按键后,触发唤醒,这里以其中的一个按键为例,其逻辑如下:
1.2.2 应用程序
应用程序不需要修改,还使用之前的轮询读取的方式,为了在测试时看出阻塞与非阻塞方式的区别,在read函数前后添加打印,如果程序运行正常,会先打印read前一句的打印,直到有按键按下后,read函数才被接触阻塞,read后一句的打印才会打印出。
1.2 实验
和之前一样,使用Makefile编译驱动程序和应用程序,并复制到nfs根文件系统中。
开始测试,按如下图,当没有按键按下时,应用程序被阻塞:

按键程序在后台运行,此时使用top指令开查看CPU的使用率,可以发现阻塞式按键驱动这种方式,CPU的暂用率几乎为0,虽然按键应用程序中仍实现循环读取的方式,但因平时读取不到按键值,按键应用程序被阻塞住了,CPU的使用权被让出,自然CPU的使用率就降下来了。

2 非阻塞I/O方式的按键检测
按键应用程序以非阻塞的方式读取,按键驱动程序也要以非阻塞的方式立即返回。应用程序可以通过select、poll或epoll函数来 查询设备是否可以操作,驱动程序使用poll函数。
2.1 非阻塞I/O之select/poll
select函数原型:
其中超时时间使用结构体timeval表示:
当timeout为NULL的时候就表示无限等待。
poll函数原型:
2.2 非阻塞I/O程序编写
2.2.1 驱动程序
poll函数处理部分:
read函数处理部分:
2.2.2 应用程序
2.2.2.1 poll方式读取
注意open函数的参数是O_NONBLOCK,即非阻塞访问,并且为了在测试时看出阻塞读取与非阻塞读取的区别,在poll函数前后添加打印,如果程序正常运行,poll函数则不会被阻塞,500ms超时未读取到按键值后会再次循环读取,实际效果就是可以看打一直有打印输出。
2.2.2.2 select方式读取
select方式读取与poll方式类似,都是非阻塞读取,程序类似:
2.3 实验
2.3.1 poll方式读取
和之前一样,使用Makefile编译驱动程序和应用程序,并复制到nfs根文件系统中。
开始测试,按如下图,当没有按键按下时,应用程序也没有被阻塞,从不断的打印就可以看出应用程序在循环运行。当有按键按下时,能够读取到对应的按键值。

按键程序在后台运行,此时使用top指令开查看CPU的使用率,可以发现非阻塞式按键驱动这种方式,CPU的暂用率也几乎为0,虽然按键应用程序中仍实现循环读取的方式,但poll函数有500ms的超时设置,在超时等待的时间里,CPU的使用权也是被让出,所以CPU的使用率也降下来了。

2.3.2 select方式读取
select方式读取与poll方式读取的效果一样。
使用ps指令查看poll方式的按键进行号,使用kill杀带该进程,再运行select方式的按键应用程序:

select非阻塞读取的方式,CPU的暂用率也几乎为0:

3 总结
本篇使用两种I/O模型进行按键读取:阻塞式I/O和非用阻塞式I/O