Linux之poll機制分析


應用程序訪問1個設備文件時可用阻塞/非阻塞方式.如果是使用阻塞方式,則直接調用open()、read()、write(),但是在驅動程序層會判斷是否可讀/可寫,如果不可讀/不可寫,則將當前進程休眠,直

到被喚醒。如果是使用非阻塞方式,就需要采用poll/select機制,而且打開文件時標記文件的訪問權限位為O_NONBLOCK。

1 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 

FD_CLR(inr fd,fd_set* set);用來清除描述詞組set中相關fd 的位

FD_ISSET(int fd,fd_set *set);用來測試描述詞組set中相關fd 的位是否為真

FD_SET(int fd,fd_set*set);用來設置描述詞組set中相關fd的位

FD_ZERO(fd_set *set);用來清除描述詞組set的全部位

如果參數timeout設為:NULL:則表示select()沒有timeout,select將一直被阻塞,直到某個文件描述符上發生了事件。0:僅檢測描述符集合的狀態,然后立即返回,並不等待外部事件的發生。

特定的時間值:如果在指定的時間段里沒有事件發生,select將超時返回。

1 int poll(struct pollfd *fds, nfds_t nfds, int timeout);這兩個函數其實本質類似.

fds 可以傳遞多個結構體,也就是說可以監測多個驅動設備所產生的事件,只要有一個產生了請求事件,就能立即返回

  struct pollfd {

    int fd; /* 文件描述符 */

    short events; /* 請求的事件類型,監視驅動文件的事件掩碼 */

    short revents; /* 驅動文件實際返回的事件 */

  } ;

nfds 監測驅動文件的個數

timeout 超時時間,單位為ms

事件類型events 可以為下列值:

POLLIN 有數據可讀

POLLRDNORM 有普通數據可讀,等效與POLLIN

POLLPRI 有緊迫數據可讀

POLLOUT 寫數據不會導致阻塞

POLLER 指定的文件描述符發生錯誤

POLLHUP 指定的文件描述符掛起事件

POLLNVAL 無效的請求,打不開指定的文件描述符

返回值

有事件發生 返回revents域不為0的文件描述符個數(也就是說事件發生,或者錯誤報告)

超時        返回0;

失敗   返回-1,並設置errno為錯誤類型

理解select模型:

理解select模型的關鍵在於理解fd_set,為說明方便,取fd_set長度為1字節,fd_set中的每個bit 可以對應一個文件描述符fd。則1字節長的fd_set最大可以對應8個fd。

(1)執行fd_set set; FD_ZERO(&set);則set用位表示是0000,0000。

(2)若fd=5,執行FD_SET(fd,&set);后set變為0001,0000(第5位置為1)

(3)若再加入fd=2,fd=1,則set變為0001,0011

(4)執行select(6,&set,0,0,0)阻塞等待

(5)若fd=1,fd=2上都發生可讀事件,則select返回,此時set變為0000,0011。

注意:沒有事件 發生的fd=5被清空。

從內核態理解poll機制

我們從應用程序直接調用poll函數,系統會走以下流程

 1 app:poll
 2 kernel:sys_poll
 3             do_sys_poll
 4                 poll_initwait(&table)
 5                 do_poll(nfds, head, &table, timeout)
 6                         for (;;) {
 7                             for (; pfd != pfd_end; pfd++) {    /* 可以監測多個驅動設備所產生的事件 */
 8                                 if (do_pollfd(pfd, pt)) {   
 9                                     count++;
10                                     pt = NULL;
11                                 }
12                                 if (count || !*timeout || signal_pending(current))
13                                     break;
14                                 __timeout = schedule_timeout(__timeout);
15                             }
16                         }
17 
18 
19 do_pollfd(pfd, pt){
20 ...
21 if (file->f_op && file->f_op->poll)
22     mask = file->f_op->poll(file, pwait);
23     return mask;                
24 ...
25 }

使用poll_initwait(&table),就是將__pollwait設為回調函數,后面會去調用驅動程序的poll函數,poll函數調用pollwait就等於調用__pollwait,將當前進程加入到等待隊列中。然后一直在循環,do_pollfd就是去

調用驅動程序的poll函數,poll函數開始調用pollwait就等於調用__pollwait回調函數,將當前進程加入到等待隊列中,以便喚醒休眠后的當前進程。然后返回當前驅動設備的狀態(mask). 如果do_pollfd返回的

mask為非0,即count非0,就會馬上返回,應用程序就可以使用FD_ISSET了解此時設備狀態。當然,如果超時或者此進程有其他信號要處理,也會馬上返回,但是應用程序使用FD_ISSET了解到此時設備狀

態還是不可用時,又繼續輪詢。如果do_pollfd返回的mask為0,而且未超時且未有其他信號發生,就會進程調度,讓此進程休眠。在前面已經將此進程加入到驅動程序的等待隊列中了,如果設備可用時,就會

喚醒等待隊列中的進程,也就喚醒了此進程,又去do_pollfd(pfd, pt)。

 

實例:基於<<Linux設備驅動開發詳解:基於最新的Linux4.0內核.pdf>>第8.2章節

驅動程序的poll函數

 1 static unsigned int globalfifo_poll(struct file * filp, poll_table * wait)
 2  {
 3       unsigned int mask =0;
 4       struct globalfifo_dev *dev = filp->private_data;
 5   
 6       mutex_lock(&dev->mutex);
 7   
 8       poll_wait(filp, &dev->w_wait, wait);//將當前進程加入到寫等待隊列 
 9       poll_wait(filp, &dev->r_wait, wait); //將當前進程加入到讀等待隊列 
10  
11      if(dev->current_len != 0)
12          mask |=POLLIN | POLLRDNORM;//當有數據時,報告可讀狀態
13      if(dev->current_len != GLOBALMEM_SIZE)
14          mask |=POLLOUT | POLLWRNORM;//當緩沖區未滿時,報告可寫狀態
15  
16      mutex_unlock(&dev->mutex);
17      return mask;
18  }

使用select監控globalfifo是否可非阻塞讀、 寫的應用程序

 1 #include "stdio.h"
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <sys/time.h>
 6 #include <unistd.h>
 7 #include <errno.h>
 8 #define FIFO_CLEAR (0x01)
 9 void main(int argc, char **argv)
10 {
11     int fd;
12     int err;
13     fd_set rfds,wfds;
14     struct timeval timeout;
15     timeout.tv_sec = 5; //設置超時時間為5s 16 
17     fd = open("/dev/globalfifo", O_RDONLY | O_NONBLOCK);
18     if(fd == -1)
19         printf("open fail\n");
20     else{
21          if (ioctl(fd, FIFO_CLEAR, 0) < 0)
22              printf("ioctl command failed\n");
23          while(1){
24             FD_ZERO(&rfds);
25             FD_ZERO(&wfds);
26             FD_SET(fd,&rfds);
27             FD_SET(fd,&wfds);
28 
29             err = select(fd+1, &rfds, &wfds, NULL, &timeout);
30             if(err == -1)
31                 printf("select fail:0x%x\n",errno);
32             if(FD_ISSET(fd, &rfds))
33                 printf("Poll monitor:can be read\n");
34             if(FD_ISSET(fd, &wfds))
35                 printf("Poll monitor:can be write\n");
36          }
37     }
38     return 0;
39 }

驅動程序全部源碼:

  1 #include <linux/module.h>
  2 #include <linux/fs.h>
  3 #include <linux/init.h>
  4 #include <linux/cdev.h>
  5 #include <linux/slab.h>
  6 #include <linux/uaccess.h>
  7 #include <linux/poll.h>
  8 
  9 //#define GLOBALMEM_SIZE 0x1000
 10 #define GLOBALMEM_SIZE 0x10
 11 #define GLOBALMEM_MAJOR 230
 12 #define GLOBALMEM_MAGIC 'g'
 13 //#define MEM_CLEAR _IO(GLOBALMEM_MAGIC,0)
 14 #define MEM_CLEAR (0x01)
 15 static int globalfifo_major = GLOBALMEM_MAJOR;
 16 module_param(globalfifo_major, int, S_IRUGO);
 17 
 18 struct globalfifo_dev {
 19  struct cdev cdev;
 20  unsigned int current_len;
 21  unsigned char mem[GLOBALMEM_SIZE];
 22  struct mutex mutex;
 23  wait_queue_head_t r_wait;
 24  wait_queue_head_t w_wait;
 25 };
 26 
 27 struct globalfifo_dev *globalfifo_devp;
 28 
 29 static int globalfifo_open(struct inode *inode, struct file *filp)
 30 {
 31     filp->private_data = globalfifo_devp;
 32     return 0;
 33 }
 34 static int globalfifo_release(struct inode *inode, struct file *filp)
 35 {
 36     return 0;
 37 }
 38 static ssize_t globalfifo_read(struct file *filp, char __user * buf, size_t size,
 39  loff_t * ppos)
 40 {
 41     unsigned int count = size;
 42     int ret = 0;
 43     struct globalfifo_dev *dev = filp->private_data;
 44     DECLARE_WAITQUEUE(wait, current);
 45 
 46     mutex_lock(&dev->mutex);
 47     add_wait_queue(&dev->r_wait, &wait);
 48 
 49     while(dev->current_len ==0){
 50         if(filp->f_flags & O_NONBLOCK){
 51             ret = -EAGAIN;
 52             goto out;
 53         }
 54 
 55         set_current_state(TASK_INTERRUPTIBLE);
 56         mutex_unlock(&dev->mutex);
 57 
 58         schedule();
 59         if(signal_pending(current)){
 60             ret = -ERESTARTSYS;
 61             goto out2;
 62         }
 63         mutex_lock(&dev->mutex);
 64 
 65     }
 66 
 67     if (count > dev->current_len)
 68         count = dev->current_len;
 69 
 70     if (copy_to_user(buf, dev->mem, count)) {
 71         ret = -EFAULT;
 72         goto out;
 73     } else {
 74         memcpy(dev->mem, dev->mem+count, dev->current_len - count);
 75         dev->current_len -=count;
 76         printk(KERN_INFO "read %d bytes(s) current_len %d\n", count, dev->current_len);
 77         wake_up_interruptible(&dev->w_wait);
 78         ret = count;
 79     }
 80 
 81 out:
 82     mutex_unlock(&dev->mutex);
 83 out2:
 84     remove_wait_queue(&dev->r_wait, &wait);
 85     set_current_state(TASK_RUNNING);
 86 
 87  return ret;
 88 }
 89 
 90 static ssize_t globalfifo_write(struct file *filp, const char __user * buf,
 91  size_t size, loff_t * ppos)
 92 {
 93     unsigned int count = size;
 94     int ret = 0;
 95     struct globalfifo_dev *dev = filp->private_data;
 96     DECLARE_WAITQUEUE(wait, current);
 97 
 98     mutex_lock(&dev->mutex);
 99     add_wait_queue(&dev->w_wait, &wait);
100 
101     while(dev->current_len == GLOBALMEM_SIZE){
102         if(filp->f_flags & O_NONBLOCK){
103             ret = -EAGAIN;
104             goto out;
105         }
106 
107         set_current_state(TASK_INTERRUPTIBLE);
108         mutex_unlock(&dev->mutex);
109         schedule();
110         if(signal_pending(current)){
111             ret = -ERESTARTSYS;
112             goto out2;
113         }
114         mutex_lock(&dev->mutex);
115 
116     }
117 
118     if (count > (GLOBALMEM_SIZE - dev->current_len))
119         count = (GLOBALMEM_SIZE - dev->current_len);
120 
121     if (copy_from_user(dev->mem + dev->current_len, buf, count)){
122         ret = -EFAULT;
123         goto out;
124     }
125     else {
126         dev->current_len += count;
127         wake_up_interruptible(&dev->r_wait);
128         ret = count;
129         printk(KERN_INFO "written %d bytes(s) current_len %d\n", count, dev->current_len);
130     }
131 out:
132     mutex_unlock(&dev->mutex);
133 out2:
134     remove_wait_queue(&dev->w_wait, &wait);
135     set_current_state(TASK_RUNNING);
136  return ret;
137 }
138 static loff_t globalfifo_llseek(struct file *filp, loff_t offset, int orig)
139 {
140     loff_t ret = 0;
141     switch (orig) {
142     case 0: /* ´ÓÎļþ¿ªÍ·Î»ÖÃseek */
143     if (offset< 0) {
144         ret = -EINVAL;
145         break;
146     }
147     if ((unsigned int)offset > GLOBALMEM_SIZE) {
148         ret = -EINVAL;
149         break;
150     }
151     filp->f_pos = (unsigned int)offset;
152     ret = filp->f_pos;
153     break;
154     case 1: /* ´ÓÎļþµ±Ç°Î»ÖÿªÊ¼seek */
155     if ((filp->f_pos + offset) > GLOBALMEM_SIZE) {
156         ret = -EINVAL;
157         break;
158     }
159     if ((filp->f_pos + offset) < 0) {
160         ret = -EINVAL;
161         break;
162     }
163     filp->f_pos += offset;
164     ret = filp->f_pos;
165     break;
166     default:
167     ret = -EINVAL;
168     break;
169     }
170     return ret;
171 }
172 static long globalfifo_ioctl(struct file *filp, unsigned int cmd,
173  unsigned long arg)
174 {
175     struct globalfifo_dev *dev = filp->private_data;
176     switch (cmd) {
177     case MEM_CLEAR:
178         mutex_lock(&dev->mutex);
179         memset(dev->mem, 0, GLOBALMEM_SIZE);
180     dev->current_len =0;
181         printk(KERN_INFO "globalfifo is set to zero\n");
182         mutex_unlock(&dev->mutex);
183         break;
184     default:
185     return -EINVAL;
186  }
187 
188  return 0;
189 }
190 static unsigned int globalfifo_poll(struct file * filp, poll_table * wait)
191 {
192     unsigned int mask =0;
193     struct globalfifo_dev *dev = filp->private_data;
194 
195     mutex_lock(&dev->mutex);
196 
197     poll_wait(filp, &dev->w_wait, wait);
198     poll_wait(filp, &dev->r_wait, wait);
199 
200     if(dev->current_len != 0)
201         mask |=POLLIN | POLLRDNORM;
202     if(dev->current_len != GLOBALMEM_SIZE)
203         mask |=POLLOUT | POLLWRNORM;
204 
205     mutex_unlock(&dev->mutex);
206     return mask;
207 }
208 
209 
210 static const struct file_operations globalfifo_fops = {
211  .owner          = THIS_MODULE,
212  .llseek         = globalfifo_llseek,
213  .read           = globalfifo_read,
214  .write          = globalfifo_write,
215  .unlocked_ioctl = globalfifo_ioctl,
216  .open           = globalfifo_open,
217  .poll           = globalfifo_poll,
218  .release        = globalfifo_release,
219 };
220 static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
221 {
222     int err, devno = MKDEV(globalfifo_major, index);
223     cdev_init(&dev->cdev, &globalfifo_fops);
224     dev->cdev.owner = THIS_MODULE;
225     err = cdev_add(&dev->cdev, devno, 1);
226     if (err)
227     printk(KERN_NOTICE "Error %d adding globalfifo%d", err, index);
228 }
229 static int __init globalfifo_init(void)
230 {
231     int ret;
232     dev_t devno = MKDEV(globalfifo_major, 0);
233 
234     if (globalfifo_major)
235     ret = register_chrdev_region(devno, 1, "globalfifo");
236     else {
237     ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
238     globalfifo_major = MAJOR(devno);
239     }
240     if (ret < 0)
241     return ret;
242 
243     globalfifo_devp = kzalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
244     if (!globalfifo_devp) {
245     ret = -ENOMEM;
246     goto fail_malloc;
247     }
248     globalfifo_setup_cdev(globalfifo_devp, 0);
249 
250     mutex_init(&globalfifo_devp->mutex);
251     init_waitqueue_head(&globalfifo_devp->r_wait);
252     init_waitqueue_head(&globalfifo_devp->w_wait);
253     return 0;
254 
255 fail_malloc:
256     unregister_chrdev_region(devno, 1);
257     return ret;
258 }
259 
260 static void __exit globalfifo_exit(void)
261 {
262     cdev_del(&globalfifo_devp->cdev);
263     kfree(globalfifo_devp);
264     unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1);
265 }
266 module_init(globalfifo_init);
267 module_exit(globalfifo_exit);
268 
269 MODULE_LICENSE("GPL v2");
View Code

測試:將應用程序設置為后台執行。

當無數據時.

當有數據時但未滿時.

當數據滿時.

 

此文源碼基於內核源碼版本為linux-2.6.22.6

參考:https://www.cnblogs.com/amanlikethis/p/6915485.html

          http://www.cnblogs.com/shihaochangeworld/p/5747490.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM