歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

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);  

Copyright © Linux教程網 All Rights Reserved