驱动程序开发:阻塞与非阻塞IO
这里写自定义目录标题
- 一、关于阻塞与非阻塞IO的基础
- Ⅰ、阻塞IO访问
- Ⅱ、非阻塞IO访问
- 二、实验(根据上一篇按键中断实验改)
- Ⅰ、阻塞方式实验
- Ⅱ、非阻塞方式实验
- 1、驱动程序
- 2、APP应用程序
- 3、关于select函数非阻塞APP示例程序
- 4、关于poll函数非阻塞APP示例程序
一、关于阻塞与非阻塞IO的基础
当应用程序对设备驱动进行操作的时候,如果不能获取到设备资源,那么阻塞式 IO 就会将应用程序对应的线程挂起,直到设备资源可以获取为止。
对于非阻塞 IO,应用程序对应的线程不会挂起,它要么一直轮询等待,直到设备资源可以使用,要么就直接放弃。
Ⅰ、阻塞IO访问
阻塞访问最大的好处就是当设备文件不可操作的时候进程可以进入休眠态,这样可以将CPU 资源让出来。而Linux 内核提供了等待队列(wait queue)来实现阻塞进程的唤醒工作。以下有几个接口函数:
1、 等待队列头:
void init_waitqueue_head(wait_queue_head_t *q)
或 宏 DECLARE_WAIT_QUEUE_HEAD
初始化等待队列头。
2、 等待队列项:
struct wait_queue_t结构体
或 宏 DECLARE_WAITQUEUE(name, tsk)
等待队列头就是一个等待队列的头部,每个访问设备的进程都是一个队列项,当设备不可用的时候就要将这些进程对应的等待队列项添加到等待队列里面。
3、 将队列项添加/移除等待队列头:
id add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
和 void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
当设备不可访问的时候就需要将进程对应的等待队列项添加到前面创建的等待队列头中,只有添加到等待队列头中以后进程才能进入休眠态。当设备可以访问以后再将进程对应的等待队列项从等待队列头中移除即可。
4、 等待唤醒:
void wake_up(wait_queue_head_t *q)
或 void wake_up_interruptible(wait_queue_head_t *q)
当设备可以使用的时候就要唤醒进入休眠态的进程,第一个可以唤醒两种状态进程,第二种只有一种。
5、 等待事件:
wait_event(wq, condition)
或wait_event_timeout(wq, condition, timeout)
或wait_event_interruptible(wq, condition)
或wait_event_interruptible_timeout(wq, condition, timeout)
设置等待队列等待某个事件,当这个事件满足以后就自动唤醒等待队列中的进程。
Ⅱ、非阻塞IO访问
用户应用程序以非阻塞的方式访问设备,设备驱动程序就要提供非阻塞的处理方式,也就是轮询。当应用程序调用 select、 epoll 或 poll 函数的时候设备驱动程序中的 poll 函数就会执行。
1、 select 函数:
int select(int nfds,fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
2、poll 函数:
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
在单个线程中,select 函数能够监视的文件描述符数量有最大的限制,一般为 1024,可以修改内核将监视的文件描述符数量改大,但是这样会降低效率!这个时候就可以使用 poll 函数,poll 函数本质上和 select 没有太大的差别,但是 poll 函数没有最大文件描述符限制。
3、 epoll 函数:
int epoll_create(int size)
创建一个 epoll 句柄。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
添加要监视的文件描述符以及监视的事件。
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
待事件的发生。
4、Linux驱动下的poll操作函数
当应用程序调用 select 或 poll 函数来对驱动程序进行非阻塞访问的时候,驱动程序file_operations 操作集中的 poll 函数就会执行。
unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait)
void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
二、实验(根据上一篇按键中断实验改)
Ⅰ、阻塞方式实验
wait_event_interruptible函数与下面功能是等效的。
1. /* 如果没有按下就将但前线程进入休眠状态(阻塞) */
2. if(!atomic_read(&dev->release)) {
3. /* 定义一个等待队列项,param1:队列名字,param2:当前任务(进程) */
4. DECLARE_WAITQUEUE(wait, current);
5.
6. /* 进入休眠状态 */
7. add_wait_queue(&dev->r_wait,&wait); //将队列项添加到等待队列头
8. __set_current_state(TASK_INTERRUPTIBLE); //当前进程设置为可被打断状态
9. schedule(); //调度器切换,将队列中的进程进入休眠状态,
10. //它会将当前进程从runqueue队列中删除掉,
11. //使其进程不再参与调度,除非使用wake_up让其重新参与调度
12.
13. /* 判断当前进程是否有信号处理,返回值不为0的话表示有信号需要处理,
14. 这个信号不是指按键信号,指例如kill -9这些命令信号
15. */
16. if(signal_pending(current)) {
17. printk("signal_pending\r\n");
18. ret = -ERESTARTSYS;
19. goto data_error;
20. }
Ⅱ、非阻塞方式实验
1、驱动程序
驱动程序修改如下:
1. static ssize_t keyirq_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) {
2. int ret = 0;
3. unsigned char keyvalue;
4. unsigned char release;
5. /* 提取私有属性 */
6. struct keyirq_dev *dev = filp->private_data;
7.
8. /* 5.3 判断是阻塞还是非阻塞方式读取驱动文件 */
9. if(filp->f_flags & O_NONBLOCK) {
10. /* 非阻塞 */
11. if(atomic_read(&dev->release) == 0) {
12. /* 按键值无效 */
13. return -EAGAIN;
14. }
15. } else {
16. /* 阻塞 */
17. wait_event_interruptible(dev->r_wait,atomic_read(&dev->release)); /* 这个可以被信号打断 */
18. }
19.
20. keyvalue = atomic_read(&dev->keyvalue);
21. release = atomic_read(&dev->release);
22.
23. if(release) {
24. if(keyvalue & 0X80) {
25. keyvalue &= ~0X80;
26. ret = copy_to_user(buf,&keyvalue,sizeof(keyvalue));
27. } else {
28. goto data_error;
29. }
30. atomic_set(&dev->release,0); // 按下标志清零
31. } else {
32. ret = -EINVAL;
33. goto data_error;
34. }
35.
36. return 0;
37. data_error:
38. return ret;
39. }
2、APP应用程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/select.h>/** argc:应用程序参数个数 * argv[]:具体打参数内容,字符串形式 * ./timerAPP <filename>* ./timerAPP /dev/keyirq*//*
* @description : main 主程序
* @param - argc : argv 数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{fd_set readfds; /* 读操作文件描述符集 */struct timeval timeout; /* 超时结构体 */int fd, ret; /* 要监视的文件描述符 */char *filename;unsigned char data;/* 判断输入的元素个数 */if(argc != 2) {printf("ERROR USAGE!\r\n");return -1;}filename = argv[1]; //获取驱动文件的路径fd = open(filename,O_RDWR | O_NONBLOCK); //根据文件路径以读写方式打开文件,非阻塞式访问if(fd < 0) {printf("file %s open failed!\r\n",filename);return -1;}/* 循环读取按键值 */while(1) {FD_ZERO(&readfds); /* 清除readfds */FD_SET(fd, &readfds); /* 将fd添加到readfds里面 *//* 构造超时时间 */timeout.tv_sec = 0;timeout.tv_usec = 500000; /* 500ms */ret = select(fd + 1,&readfds,NULL,NULL,&timeout);switch (ret){case 0: /* 超时 */printf("timeout!\r\n");break;case -1: /* 错误 */printf("error!\r\n");break; default: /* 其他的可以读取数据 */if(FD_ISSET(fd,&readfds)) {ret = read(fd,&data,sizeof(data));/* 判断是否为fd文件描述符 */if (ret < 0){} else {if(data) {printf("key value = %#x\r\n",data);}}}break;} }close(fd);return 0;
}
3、关于select函数非阻塞APP示例程序
示例代码 52.1.3.1 select 函数非阻塞读访问示例
1 void main(void)
2 {3 int ret, fd; /* 要监视的文件描述符 */
4 fd_set readfds; /* 读操作文件描述符集 */
5 struct timeval timeout; /* 超时结构体 */
6 7
fd = open("dev_xxx", O_RDWR | O_NONBLOCK); /* 非阻塞式访问 */
8 9
FD_ZERO(&readfds); /* 清除 readfds */
10 FD_SET(fd, &readfds); /* 将 fd 添加到 readfds 里面 */
11
12 /* 构造超时时间 */
13 timeout.tv_sec = 0;
14 timeout.tv_usec = 500000; /* 500ms */
15
16 ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
17 switch (ret) {18 case 0: /* 超时 */
19 printf("timeout!\r\n");
20 break;
21 case -1: /* 错误 */
22 printf("error!\r\n");
23 break;
24 default: /* 可以读取数据 */
25 if(FD_ISSET(fd, &readfds)) { /* 判断是否为 fd 文件描述符 */
26 /* 使用 read 函数读取数据 */
27 }
28 break;
29 }
30 }
4、关于poll函数非阻塞APP示例程序
示例代码 52.1.3.2 poll 函数读非阻塞访问示例
1 void main(void)
2 {3 int ret;
4 int fd; /* 要监视的文件描述符 */
5 struct pollfd fds;
6 7
fd = open(filename, O_RDWR | O_NONBLOCK); /* 非阻塞式访问 */
8 9
/* 构造结构体 */
10 fds.fd = fd;
11 fds.events = POLLIN; /* 监视数据是否可以读取 */
12
13 ret = poll(&fds, 1, 500); /* 轮询文件是否可操作,超时 500ms */
14 if (ret) { /* 数据有效 */
15 ......
16 /* 读取数据 */
17 ......
18 } else if (ret == 0) { /* 超时 */
19 ......
20 } else if (ret < 0) { /* 错误 */
21 ......
22 }
23 }
驱动程序开发:阻塞与非阻塞IO相关推荐
- linux驱动开发 - 10_阻塞和非阻塞 IO
文章目录 1 阻塞和非阻塞 IO 1.1 阻塞和非阻塞简介 1.2 等待队列 1.等待队列头 2.等待队列项 3.将队列项添加/移除等待队列头 4.等待唤醒 5.等待事件 1.3 Linux驱动下的p ...
- Linux 阻塞和非阻塞IO 实验
目录 阻塞和非阻塞IO 阻塞和非阻塞简介 等待队列 轮询 Linux 驱动下的poll 操作函数 阻塞IO 实验 硬件原理图分析 实验程序编写 运行测试 非阻塞IO 实验 硬件原理图分析 实验程序编写 ...
- Linux之阻塞与非阻塞IO
目录 一.阻塞与非阻塞IO简介 1.阻塞IO 2.非阻塞IO 二.应用程序阻塞与非阻塞 1.阻塞 2.查询(非阻塞) ①select ②poll ③epoll 三.驱动程序阻塞与非阻塞 1.等待队列( ...
- Linux 阻塞和非阻塞 IO 实验
目录 一.阻塞和非阻塞简介 1.IO 概念 2.阻塞与非阻塞 二.等待队列 1.等待队列头 2.等待队列项 3.将队列项添加/移除等待队列头 4.等待唤醒 5.等待事件 三.轮询 1.应用程序的非阻塞 ...
- Linux驱动开发6 阻塞与非阻塞
阻塞和非阻塞 IO 是 Linux 驱动开发里面很常见的两种设备访问模式,在编写驱动的时候 一定要考虑到阻塞和非阻塞.本章我们就来学习一下阻塞和非阻塞 IO ,以及如何在驱动程序中 处理阻塞与非阻塞, ...
- 嵌入式Linux 阻塞和非阻塞 IO 驱动设备访问模式
阻塞和非阻塞 IO 是 Linux 驱动开发里面很常见的两种设备访问模式, 在编写驱动的时候一定要考虑到阻塞和非阻塞. 阻塞与非阻塞简介 阻塞操作是指在执行设备操作时, 若不能获得资源, 则挂起进程直 ...
- linux驱动系列学习之阻塞与非阻塞IO(六)
一. 阻塞与非阻塞IO概念 阻塞操作是指在执行设备操作时,若不能获取资源,则挂起进程进入休眠状态,等待可满足条件后进行操作.被挂起的进程从调度器队列移动到挂起队列(睡眠状态).当操作驱动程序r ...
- python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)...
python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程并行与并发同步与异步阻塞与非阻塞CPU密集型与IO密集型 线程与进程 进程 前言 ...
- 网络编程中同步与异步,IO阻塞与非阻塞总结
IO操作分两个阶段 第1个阶段:等待数据准备好(从外部设备磁盘或网络读到内核缓冲区): 第2个阶段:采用系统调用(内核进程),操作系统内核将数据从内核缓冲区读到用户空间. 第1阶段花费的时间远远大于第 ...
最新文章
- zoomImg相册大图预览插件
- MATLAB 的条件分支语句
- 扎心!天天写代码,方向真的对吗?
- word中中文保持正体,英文用斜体的方法.
- springboot动态数据源切换(多数据源配置)
- 100转换成二进制 java,一段简单的java代码,十进制转二进制
- c语言中把各位上为奇数的数取出,下列给定程序中函数fun()的功能是:将长整型数中每一位上为奇数的数依次取出,构成一个新数放在冲。 - 赏学吧...
- mysql 两张表差集_mysql中两张表使用left join on 求差集详解
- oracle 删除中文表,oracle - 删除全局临时表 - SO中文参考 - www.soinside.com
- POJ1321(深搜)
- 硅谷35岁以后的程序员都在做什么?
- 有同学问我:Fetch 和 Ajax 有什么区别?
- Ubuntu IPFS小白安装入门教程
- Dart教程(三):类的定义和使用
- html杜邦分析图,怎么用Excel做动态杜邦分析图表?
- indy-sdk tutorials数字身份认证(一)
- iOS 内购提示不允许App内购买项目,打开内购方式和检测不允许内购的方法。
- CICD之 gitlab和gtilab runner
- 笨方法学python 习题29-31
- vs2019无法打开包括文件