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


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

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

基本概念:

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

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

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

  • 1
  • 2
  • 3
  • 下一页

相关内容