1. 程式人生 > >Linux裝置驅動中的阻塞與非阻塞I/O

Linux裝置驅動中的阻塞與非阻塞I/O

阻塞和非阻塞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  *<
[email protected]
>. 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

阻塞和非阻塞I/O是裝置訪問的兩種不同模式,驅動程式可以靈活的支援使用者空間對裝置的這兩種訪問方式 本例子講述了這兩者的區別 並實現I/O的等待佇列機制, 並進行了使用者空間的驗證 基本概念: 1> 阻塞操作      是指 在執行裝置操作時,若不能獲得資源,則掛起程

linux裝置驅動阻塞阻塞(一)

以上是驅動中的讀取和寫入操作,當寫程序發現數據已滿,不能寫入時,且上層應用是以阻塞的方式開啟裝置檔案時,所以必須要寫入資料才能返回,否則不能返回,那麼就有兩種實現機制,要不就是不停地忙等待,等待裝置可以寫入時,便寫入,然後返回,可是這樣做的話,非常影響CPU的執行效率,大大降低了CPU的效能,所以linux核

Linux 裝置驅動阻塞阻塞I/O

何謂阻塞與非阻塞I/O?簡單來說就是對I/O操作的兩種不同的方式。 阻塞:當操作I/O裝置時,如果不能得到相應的資源,則該程序進入休眠狀態,被從排程器的佇列中移走,直到等待的條件滿足。 阻塞的讀取一個字元: char buf; fd = open("/dev/ttyS

深入淺出:Linux裝置驅動阻塞阻塞I/O

今天寫的是Linux裝置驅動中的阻塞和非阻塞I/0,何謂阻塞與非阻塞I/O?簡單來說就是對I/O操作的兩種不同的方式,驅動程式可以靈活的支援使用者空間對裝置的這兩種訪問方式。 一、基本概念: 阻塞操作 : 是指在執行裝置操作時,若不能獲得資源,則掛起程序直到

linux裝置驅動阻塞阻塞IO

一、等待佇列 1、定義等待佇列頭 wait_queue_head_t my_queue; 2、初始化等待佇列頭 init_waitqueue_head(&my_queue); 3、定義等待佇列 DECLARE_WAITQUEUE(name,tsk); 4、新增/移除

linux裝置驅動阻塞阻塞I/O

先做一下與核心阻塞有關的知識儲備: 1)程序休眠:     程序休眠,簡單的說就是正在執行的程序讓出CPU。休眠的程序會被核心擱置在在一邊,只有當核心再次把休眠的程序喚醒,程序才會會重新在CPU執行。這是核心中的程序排程。一個CPU在同一時間只能有一個程序在執行,微觀序列巨

linux裝置阻塞阻塞io

1:阻塞與非阻塞io 阻塞操作:執行裝置操作時若不能獲得資源則掛起程序(程序進入休眠狀態,將cpu資源讓給其他程序),知道滿足條件後在執行!喚醒睡眠的程序的地方最大可能發生在中斷中,因為硬體資源的獲得一般伴隨著一箇中斷。 非阻塞:程序不能進行裝置操作時並不掛起,它或者放棄,

Socket編程阻塞阻塞的區別

軟件 復用 優點 調用 服務器 運用 需要 默認 con   阻塞:一般的I/O操作可以在新建的流中運用.在服務器回應前它等待客戶端發送一個空白的行.當會話結束時,服務器關閉流和客戶端socket.如果在隊列中沒有請示將會出現什麽情況呢?那個方法將會等待一個的到來.這個行為

關於veriolg阻塞阻塞賦值問題

觸發 改變 希望 到來 決定 工作 執行 為什麽 個人 在一開始學到阻塞和非阻塞的時候,所被告知的兩者的區別就在於阻塞是串行的,非阻塞是並行的。但是雖然知道這個不同點,有些時候還是很難真正區分用兩者電路的區別,在這就通過幾個例子來解釋一下。 以一個簡單的串行流水線寄存器為例

Linux阻塞阻塞,同步非同步的關係及IO模型

一、阻塞與非阻塞,同步與非同步的關係 1、同步   同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。也就是說事情必須一件一件地做,等前一件做完了才能做下一件事。 2、非同步   非同步,就是在發出一個功能呼叫時,呼叫者不會立刻得到結果。實際處理這個呼叫的部

Linux read 阻塞阻塞讀取

read函式非阻塞讀取會立即返回。 阻塞讀取通過設定也可在沒有資料時立即返回 #include <termios.h> struct termios tio; tio.c_cc[VTIME] = 0; // timeout in deciseconds for noncanonical rea

網路程式設計阻塞阻塞、同步非同步、I/O模型的理解

1. 概念理解      在進行網路程式設計時,我們常常見到同步(Sync)/非同步(Async),阻塞(Block)/非阻塞(Unblock)四種呼叫方式:同步:所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。也就是必須一件一件事做,等前一件做完了才能做下一件事。 例如

簡述linux同步非同步、阻塞阻塞概念以及五種IO模型

1、概念剖析 相信很多從事linux後臺開發工作的都接觸過同步&非同步、阻塞&非阻塞這樣的概念,也相信都曾經產生過誤解,比如認為同步就是阻塞、非同步就是非阻塞,下面我們先剖析下這幾個概念分別是什麼含義。 同步:所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。也就是必

Linux下檔案的阻塞阻塞對部分系統呼叫的影響

1.基本概念 所謂的阻塞,即核心在對檔案操作I/O系統呼叫時,如果條件不滿足(可能需要產生I/O),則核心會將該程序掛起。非阻塞則是發現條件不滿足就會立即返回。此外需要注意的是非阻塞並不是輪詢,不然就和阻塞沒多大區別了,它只是呼叫不成功就直接返回了,不會在去看

關於Verilog HDL阻塞阻塞賦值形象理解

關於Verilog 中阻塞與非阻塞賦值的幾點理解 相信很多剛開始學習Verilog的童鞋對阻塞、非阻塞賦值理解得不是很明白,或者說是一頭霧水。確實,Verilog中阻塞、非阻塞一直就是一個難點,很多具備很久開發經驗的工程師仍是不得要領,在分析程式碼,看模擬時還

[Linux]阻塞阻塞(等待佇列、輪詢)機制

基本概念 阻塞指執行裝置操作時,不能獲得資源則掛起程序,被掛起的程序進入休眠,從排程器的進行佇列中移走。 非阻塞指在不能獲得資源的情況下,要麼放棄,要麼不停地查詢,直到可以操作。 等待佇列(Wait Queue) Linux 中採用等待佇列來實

芯靈思Sinlinx A64開發板 Linux內核等待隊列poll ---阻塞阻塞

打開 inux 平臺 讀取數據 判斷 函數 方式 動作 lock 開發平臺 芯靈思Sinlinx A64內存: 1GB 存儲: 4GB開發板詳細參數 https://m.tb.cn/h.3wMaSKm開發板交流群 641395230 阻塞:阻塞調用是指調用結果返回之前

同步異步、阻塞阻塞

阻塞與非阻塞 就會 結束 檢查 通信機制 得到 node 分布 好書 “阻塞”與"非阻塞"與"同步"與“異步"不能簡單的從字面理解,提供一個從分布式系統角度的回答。1.同步與異步同步和異步關註的是消息通信機制 (synchronous communication/ a

阻塞阻塞,同步異步

通過 部件 一個 socket 沒有 事件觸發 sel syn 就會 在進行網絡編程時,我們常常見到同步(Sync)/異步(Async),阻塞(Block)/非阻塞(Unblock)四種調用方式:同步: 所謂同步,就是在發出一個功能調用時,在沒有得到結果之前,

同步異步,阻塞阻塞

消息 阻塞 結果 阻塞與非阻塞 過程調用 函數 異步 非阻塞 完成 異步的概念和同步相對。當一個同步調用發出後,調用者要一直等待返回消息(結果)通知後,才能進行後續的執行;當一個異步過程調用發出後,調用者不能立刻得到返回消息(結果)。實際處理這個調用的部件在完成後,通過狀態