阻塞和非阻塞I/O是设备访问的两种不同模式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式

本例子讲述了这两者的区别 并实现I/O的等待队列机制, 并进行了用户空间的验证

基本概念:

1> 阻塞操作      是指 在执行设备操作时,若不能获得资源,则挂起进程直到满足操作条件后再进行操作。被挂起的进 程进入休眠,  被从调度器移走,直到条件满足。

2> 非阻塞操作  在不能进行设备操作时,并不挂起,它或者放弃,或者不停地查询,直到可以进行操作。非阻塞应用  程序通常使   用select系统调用查询是否可以对设备进行无阻塞的访问最终会引发设备驱动中poll函数执行。

  1 /*
2  * a globalfifo driver as example of char device drivers
3  * This example is to introduce poll, blocking and non-blocking access
4  *
5  *The initial developer of the original code is Baohua Song
6  *<auther@linuxdriver.cn>. All Rights Reserved
7  *
8  * 1>只当FIFO 中有数据时(有进程把数据写到这个 FIFO而且没有被 读进程 读空)
9  *   读进程才能把数据读出,读出后数据 从 FIFO 中拿掉
10  * 2>只有当FIFO 非满时(即还有空间未被读写或满后被读进程读出了数据)
11  *   写进程才能往里面写数据,
12  * 这样 读唤醒写 写唤醒读
13  */
14
15 #include<linux/module.h>
16 #include<linux/types.h>
17 #include<linux/fs.h>
18 #include<linux/errno.h>
19 #include<linux/mm.h>
20 #include<linux/sched.h>
21 #include<linux/init.h>
22 #include<linux/cdev.h>
23 #include<asm/io.h>
24 #include<asm/system.h>
25 #include<asm/uaccess.h>
26 #include<linux/poll.h>
27
28 #define GLOBALFIFO_SIZE  10            /*全局fifo最大10字节 不要太大 方便写满测试*/
29 #define FIFO_CLEAR 0X1                  /*清0全局内存的长度*/
30 #define GLOBALFIFO_MAJOR 249            /*预设的globalfifo 的主设备号*/
31
32 static int globalfifo_major = GLOBALFIFO_MAJOR;
33
34 /*globalfifo设备结构体*/
35 struct globalfifo_dev{
36     struct cdev cdev;                   /*cdev结构体*/
37     unsigned int current_len;           /*fifo有效数据长度*/
38     unsigned char mem[GLOBALFIFO_SIZE];  /*全局内存*/
39     struct semaphore sem;               /*并发控制用的信号量*/
40     wait_queue_head_t r_wait;           /*阻塞读用的等待队列 内核双向循环链表 都可以为头*/
41     wait_queue_head_t w_wait;           /*阻塞写用的等待队列头*/
42 };
43
44 struct globalfifo_dev *globalfifo_devp; /*设备结构体指针*/
45
46 /*globalfifo读函数*/
47 static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
48 {
49     int ret;
50     struct globalfifo_dev *dev = filp->private_data;
51     DECLARE_WAITQUEUE(wait, current);
52
53     down(&dev->sem);                     /*获得信号量*/
54     add_wait_queue(&dev->r_wait, &wait); /*加入读等待队列头 到内核*/
55
56     /*等待FIFO 非空*/
57     if(dev->current_len == 0){
58         if(filp->f_flags & O_NONBLOCK){   /*如果进程为 非阻塞打开 设备文件*/
59             ret = -EAGAIN;
60             goto out;
61         }
62         __set_current_state(TASK_INTERRUPTIBLE); /*改变进程状态为睡眠*/
63         up(&dev->sem);                           /*释放信号量*/
64
65         schedule();                              /*调度其他进程执行*/
66         if(signal_pending(current)){
67                                                 /*如果是因为信号唤醒*/
68             ret = -ERESTARTSYS;
69             goto out2;
70         }
71         down(&dev->sem);
72     }
73
74     /*拷贝到用户空间*/
75     if(count > dev->current_len)
76         count = dev->current_len;
77     if(copy_to_user(buf, dev->mem, count)){
78         ret = -EFAULT;
79         goto out;
80     }else{
81         memcpy(dev->mem, dev->mem + count, dev->current_len - count);/*数据前移*/
82         dev->current_len -= count; /*有效数据长度减少*/
83         printk(KERN_INFO"read %d bytes(s),current_len:%d\n",count, dev->current_len);
84
85         wake_up_interruptible(&dev->w_wait); /*唤醒写等待队列*/
86         ret = count;
87     }
88 out:
89     up(&dev->sem); /*释放信号量*/
90 out2:
91     remove_wait_queue(&dev->w_wait, &wait); /*从属的等待队列头移除*/
92     set_current_state(TASK_RUNNING);
93     return ret;
94 }
95
96 /*globalfifo 写操作*/
97 static ssize_t globalfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
98 {
99     struct globalfifo_dev *dev = filp->private_data;
100     int ret;
101     DECLARE_WAITQUEUE(wait, current);    /*定义等待队列*/
102
103     down(&dev->sem);                     /*获得信号量*/
104     add_wait_queue(&dev->w_wait, &wait); /*进入写等待队列头*/
105
106     /*等待FIFO非满*/
107     if(dev->current_len == GLOBALFIFO_SIZE){
108         if(filp->f_flags & O_NONBLOCK){   /*如果进程非阻塞打开的文件*/
109             ret = -EAGAIN;
110             goto out;
111         }
112
113         __set_current_state(TASK_INTERRUPTIBLE); /*改变进程状态为睡眠*/
114         up(&dev->sem);                     /*释放信号量*/
115
116         schedule();                         /*调度其他进程执行*/
117         if(signal_pending(current)){
118                                             /*如果是因为信号唤醒*/
119             ret = -ERESTARTSYS;
120             goto out2;
121         }
122         down(&dev->sem);                    /*获得信号量*/
123     }
124
125     /*从用户空间拷贝数据到内核空间*/
126     if(count > GLOBALFIFO_SIZE - dev->current_len){
127         /*如果要拷贝的数据大于 剩余有效内存长度
128          *则 只拷贝最大 能装下的长度
129          */
130         count = GLOBALFIFO_SIZE - dev->current_len;
131     }
132     if(copy_from_user(dev->mem + dev->current_len, buf, count)){
133         ret = -EFAULT;
134         goto out;
135     }else {
136         dev->current_len += count;
137         printk(KERN_INFO"written %d bytes(s), current_len: %d\n",count, dev->current_len);
138
139         wake_up_interruptible(&dev->r_wait); /*唤醒读等待队列*/
140         ret = count;
141     }
142     out:
143         up(&dev->sem); /*释放信号量*/
144     out2:
145         remove_wait_queue(&dev->w_wait, &wait); /*从附属的等待队列头移除*/
146         set_current_state(TASK_RUNNING);
147         return ret;
148 }
149
150
151 /*ioctl 设备控制函数*/
152 static int globalfifo_ioctl(struct inode *inodep,struct file *filp, unsigned int cmd, unsigned long arg)
153 {
154     struct globalfifo_dev *dev = filp->private_data;/*获得设备结构体指针*/
155
156     switch(cmd){
157         case FIFO_CLEAR:
158             down(&dev->sem);                        /*获得信号量*/
159             dev->current_len = 0;
160             memset(dev->mem, 0, GLOBALFIFO_SIZE);
161             up(&dev->sem);                          /*释放信号量*/
162
163             printk(KERN_INFO"globalfifo is set to zero\n");
164             break;
165
166         default:
167             return -EINVAL;
168     }
169     return 0;
170 }
171
172 /*在驱动中的增加轮询操作*/
173 static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
174 {
175     unsigned int mask = 0;
176     struct globalfifo_dev *dev = filp->private_data;/*获得设备结构体指针*/
177
178     down(&dev->sem);
179     poll_wait(filp, &dev->r_wait, wait);
180     poll_wait(filp, &dev->w_wait, wait);
181
182     /*fifo非空*/
183     if(dev->current_len != 0){
184         mask |= POLLIN | POLLRDNORM; /*标示数据可以获得*/
185     }
186
187     /*fifo 非满*/
188     if(dev->current_len != GLOBALFIFO_SIZE){
189         mask |= POLLOUT | POLLWRNORM ; /*标示数据可以写入*/
190     }
191
192     up(&dev->sem);
193     return mask; /*返回驱动是否可读 或可写的 状态*/
194 }
195
196 /*文件打开函数*/
197 int globalfifo_open(struct inode *inode, struct file *filp)
198 {
199     /*让设备结构体作为设备的私有信息*/
200     filp->private_data = globalfifo_devp;
201     return 0;
202 }
203
204 /*文件释放函数*/
205 int globalfifo_release(struct inode *inode, struct file *filp)
206 {
207     return 0;
208 }
209
210 /*文件操作结构体*/
211 static const struct file_operations globalfifo_fops = {
212     .owner = THIS_MODULE,
213     .read = globalfifo_read,
214     .write = globalfifo_write,
215     .ioctl = globalfifo_ioctl,
216     .poll = globalfifo_poll,
217     .open = globalfifo_open,
218     .release = globalfifo_release,
219 };
220
221 /*初始化并注册cdev*/
222 static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
223 {
224     int err, devno = MKDEV(globalfifo_major, index);
225
226     cdev_init(&dev->cdev, &globalfifo_fops);
227     dev->cdev.owner = THIS_MODULE;
228     err = cdev_add(&dev->cdev, devno, 1);
229     if(err)
230         printk(KERN_NOTICE "Error %d adding LED %d", err, index);
231 }
232
233 /*设备驱动模块加载函数*/
234 int globalfifo_init(void)
235 {
236     int ret;
237     dev_t devno = MKDEV(globalfifo_major, 0);
238
239     /*申请设备号*/
240     if(globalfifo_major)
241         ret = register_chrdev_region(devno, 1, "globalfifo");
242     else{/*动态申请设备号*/
243         ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
244         globalfifo_major = MAJOR(devno);
245     }
246
247     if(ret < 0)
248         return ret;
249
250     /*动态申请设备结构体的内存*/
251     globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
252     if(!globalfifo_devp){
253         ret = - ENOMEM;
254 goto fail_malloc;
255     }
256
257     memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));
258
259     globalfifo_setup_cdev(globalfifo_devp, 0);
260
261     init_MUTEX(&globalfifo_devp->sem);              /*初始化信号量*/
262     init_waitqueue_head(&globalfifo_devp->r_wait);  /*初始化读等待队列头*/
263     init_waitqueue_head(&globalfifo_devp->w_wait);  /*初始化写等待队列头*/
264
265     return 0;
266
267 fail_malloc: unregister_chrdev_region(devno, 1);
268              return ret;
269 }
270
271 void globalfifo_exit(void)
272 {
273     cdev_del(&globalfifo_devp->cdev); /*注销cdev*/
274     kfree(globalfifo_devp); /*释放设备结构体内存*/
275     unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*释放设备号*/
276 }
277
278 MODULE_AUTHOR("Song Baohua");
279 MODULE_LICENSE("Dual BSD/GPL");
280
281 //module_param()
282
283 module_init(globalfifo_init);
284 module_exit(globalfifo_exit);

阻塞测试:

驱动的轮询操作(非阻塞) 测试程序:

  1 /*测试程序:该程序用于监控 globalfifo 的可读 可写状态(是否可以非阻塞读写)*/
2 #include<stdio.h>
3 #include<sys/select.h>
4 #include<fcntl.h>
5
6 #define FIFO_CLEAR 0X1
7 #define BUFFER_LEN 20
8
9 int main(void)
10 {
11     int fd, num;
12     char rd_ch[BUFFER_LEN];
13     fd_set rfds, wfds; /*读写文件描述符集*/
14
15     /*以非阻塞的方式打开 设备文件*/
16     fd = open("/dev/globalfifo", O_RDONLY | O_NONBLOCK);
17     if(fd != -1){
18         /*FIFO 清零*/
19         //if(ioctl(fd, FIFO_CLEAR, 0) < 0)
20         //  printf("ioctl command failed\n");
21
22         while(1){
23             FD_ZERO(&rfds); /*清除一个文件描述符集*/
24             FD_ZERO(&wfds);
25             FD_SET(fd, &rfds); /*将一个文件描述符 加入 文件描述符集中*/
26             FD_SET(fd, &wfds);
27
28 /*最高文件描述符+1 要监控的 读    写     异常  等待超时返回*/
29             select(fd + 1, &rfds, &wfds, NULL, NULL); /*该函数最终调用poll*/
30
31             /*数据可获得*/
32             if(FD_ISSET(fd, &rfds))
33                 printf("Poll monitor:can be read\n");
34
35             /*数据可写入*/
36             if(FD_ISSET(fd, &wfds))
37                 printf("Poll monitor:can be written\n");
38         }
39     }else{
40         printf("Device open failure\n");
41     }
42     return 0;
43 }

每次实验前都要先把之前后台还在运行的测试杀死以避免对接下来实验的干扰:

1>FIFO 为空 只可写

2>FIFO 为满 只可读

3>FIFO 不满 不空 可读可写

参考文献 :《LINUX设备驱动程序》 (第三版)

《设备驱动开发详解》(第二版) 宋宝华

《UNIX环境高级编程》 (第二版)

Linux设备驱动中的阻塞与非阻塞I/O相关推荐

  1. linux 设备驱动阻塞,深入浅出:Linux设备驱动中的阻塞和非阻塞I/O

    今天写的是Linux设备驱动中的阻塞和非阻塞I/0,何谓阻塞与非阻塞I/O?简单来说就是对I/O操作的两种不同的方式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式. 一.基本概念: 阻塞操作 ...

  2. linux write引起进程挂起,Linux设备驱动中的阻塞与非阻塞总结

    Linux设备驱动中的阻塞与非阻塞总结 阻塞操作是指,在执行设备操作时,若不能获得资源,则进程挂起直到满足可操作的条件再进行操作. 非阻塞操作的进程在不能进行设备操作时,并不挂起.被挂起的进程进入sl ...

  3. Linux设备驱动中的阻塞和非阻塞IO

    这篇文章我们来了解下Linux设备驱动中阻塞和非阻塞. 阻塞:阻塞是指执行设备操作时,如果不能获得设备资源,则挂起进程,是进程进入休眠模式,直到设备资源可以获取. 非阻塞:非阻塞是在不能获取设备资源时 ...

  4. Linux设备驱动中的并发控制总结

    并发(concurrency)指的是多个执行单元同时.并行被执行.而并发的执行单元对共享资源(硬件资源和软件上的全局.静态变量)的访问则容易导致竞态(race conditions).   SMP是一 ...

  5. linux 两个驱动 竞态,第7章 Linux设备驱动中的并发控制之一(并发与竞态)

    本章导读 Linux设备驱动中必须解决的一个问题是多个进程对共享资源的并发访问,并发的访问会导致竞态(竞争状态). Linux提供了多种解决竞态问题的方式,这些方式适合不同的应用场景. 7.1讲解了并 ...

  6. Linux设备驱动开发详解:第7章 Linux设备驱动中的并发控制

    7.1并发与竞态 (1).竞态的发生场景:CPU0的进程与CPU1的进程之间.CPU0的中断与CPU1的进程之间.CPU0的中断与CPU1的中断之间: (2).解决竞态问题的途径是保证对共享资源的互斥 ...

  7. Linux 设备驱动中的 I/O模型(一)—— 阻塞和非阻塞I/O

    在前面学习网络编程时,曾经学过I/O模型 Linux 系统应用编程--网络编程(I/O模型),下面学习一下I/O模型在设备驱动中的应用. 回顾一下在Unix/Linux下共有五种I/O模型,分别是: ...

  8. Linux 设备驱动中的阻塞与非阻塞 I/O

    阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作.被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足.而非阻塞操作的进程在不能进行设备操作时 ...

  9. linux设备驱动中的阻塞与非阻塞(一)

    这两天在搞linux驱动的阻塞和非阻塞,困扰了两天,看了不少博客,有了点自己的想法,也不知是否对错,但还是写写吧,让各位大神给我指点指点.       首先说说什么是阻塞和非阻塞的概念:阻塞操作就是指 ...

最新文章

  1. 102页PPT,DeepMind强化学习最新进展,含图文、公式和代码
  2. HashSet 和 LinkedHashSet 源码分析,竟如此简单!
  3. Java读写文件的几种方式
  4. Android之数据转化崩溃问题
  5. (转)Asp.net 中 Get和Post 的用法
  6. 需要多长时间达到一个本科毕业生刚毕业的水平。
  7. PHP 在服务端的应用概述
  8. hdu 4055 hdu 4489 动态规划
  9. python3 闭包
  10. SEO技巧--代码优化
  11. 如何按距离排序 php,php做附近的人,根据距离由近到远进行排序
  12. linux mysql workbench 安装_Ubuntu 20.04 上 MySQL Workbench 安装和使用图文详解
  13. ELAS_ROS算法在KITTI数据集上生成稠密点云
  14. python智能写诗的程序_【百度大脑新品体验】智能写诗与智能春联
  15. java静态代码块,构造代码块,构造函数,mian()代码执行顺序详细分析
  16. 【自用】Mybatis的学习笔记(第一天)
  17. php 字符串替换中文,PHP中文字符串替换其中为*的方法
  18. 4K(不是价格)电视机选购指南
  19. 利用Flash制作一个可以自由拨动的时钟模具
  20. 前端发起接口请求时报错:405 Method Not Allowed

热门文章

  1. Mac上android emulator模拟器使用(三十一)
  2. asp23粉末钢生锈吗_ASP-23钢材特性
  3. 全球最被看好的十大垂直搜索引擎
  4. 从零开始之驱动发开、linux驱动(六十五、内核调试篇--串口驱动)
  5. Excel 数据透视表小技巧之 04 如何将包含国家和年份的二维展开一维三列国家、年份、值(教程含数据)
  6. RedHat Yum 命令备忘单
  7. 安利一个超级好用的文档格式转换器网站,解决了我论文CAJ转PDF,还有word转PDF的问题,妥妥白piao
  8. 黑磷微米片和石墨烯的异质结
  9. 《嵌入式Linux驱动开发教程》--高级I/O操作
  10. tensorflow强化学习之打乒乓球(Reinforcement Learning)