8.中斷按鍵驅動程序之poll機制(詳解)


本節繼續在上一節中斷按鍵程序里改進,添加poll機制.

那么我們為什么還需要poll機制呢。之前的測試程序是這樣:

while (1) { read(fd, &key_val, 1); printf("key_val = 0x%x\n", key_val); }

 

在沒有poll機制的情況下,大部分時間程序都處在read中休眠的那個位置。如果我們不想讓程序停在這個位置,而是希望當有按鍵按下時,我們再去read,因此我們編寫poll函數,測試程序調用poll函數根據返回值,來決定是否執行read函數。

poll機制作用:相當於定時器,設置一定時間使進程等待資源,如果時間到了中斷還處於睡眠狀態(等待隊列),poll機制就會喚醒中斷,獲取一次資源

 

1.poll機制內核框架

如下圖所示,在用戶層上,使用poll或select函數時,和open、read那些函數一樣,也要進入內核sys_poll函數里,接下來我們分析sys_poll函數來了解poll機制(位於/fs/select.c)

 

1.1 sys_poll代碼如下:

asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,long timeout_msecs)
{
        if (timeout_msecs > 0)    //參數timeout>0
    {
         timeout_jiffies = msecs_to_jiffies(timeout_msecs);  //通過頻率來計算timeout時間需要多少計數值
    }
    else
    {
          timeout_jiffies = timeout_msecs;    //如果timeout時間為0,直接賦值
       }
  return do_sys_poll(ufds, nfds, &timeout_jiffies);   //調用do_sys_poll。
}

 

1.2 然后進入do_sys_poll(位於fs/select.c):

int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
{
  ... ...
  /*初始化一個poll_wqueues變量table*/
  poll_initwait(&table);
  ... ...
  fdcount = do_poll(nfds, head, &table, timeout);
  ... ... 
}

 

1.3進入poll_initwait函數,發現主要實現以下一句,后面會分析這里:

table ->pt-> qproc=__pollwait;    //__pollwait將在驅動的poll函數里的poll_wait函數用到

1.4然后進入do_poll函數, (位於fs/select.c):

static int do_poll(unsigned int nfds,  struct poll_list *list, struct poll_wqueues *wait,  s64 *timeout)
{
  ……
       for (;;)
   {
    ……
    set_current_state(TASK_INTERRUPTIBLE);       //設置為等待隊列狀態
    ......
       for (; pfd != pfd_end; pfd++) {             //for循環運行多個poll機制
                   /*將pfd和pt參數代入我們驅動程序里注冊的poll函數*/
                        if (do_pollfd(pfd, pt))     //若返回非0,count++,后面並退出
              {  count++;
                           pt = NULL; } }

    ……

    /*count非0(.poll函數返回非0),timeout超時計數到0,有信號在等待*/

       if (count || !*timeout || signal_pending(current))
                            break;
    ……
  
    /*進入休眠狀態,只有當timeout超時計數到0,或者被中斷喚醒才退出,*/
         __timeout = schedule_timeout(__timeout);

    ……

   }

__set_current_state(TASK_RUNNING);  //開始運行
return count;

}

 

1.4.1上面do_pollfd函數到底是怎么將pfd和pt參數代入的?代碼如下(位於fs/select.c):

static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
{
      ……
         if (file->f_op && file->f_op->poll)
         mask = file->f_op->poll(file, pwait);
      ……

return mask;
}

 

上面file->f_op 就是我們驅動里的file_oprations結構體,如下圖所示:

 

所以do_pollfd(pfd, pt)就執行了我們驅動程序里的.poll(pfd, pt)函數(第2小節開始分析.poll函數)

1.4.2當poll進入休眠狀態后,又是誰來喚醒它?這就要分析我們的驅動程序.poll函數(第2小節開始分析.poll函數)

2寫驅動程序.poll函數,並分析.poll函數:

在上一節驅動程序里添加以下代碼:

  #include <linux/poll.h>                //添加頭文件
  
/* .poll驅動函數: third_poll */ static unsigned int third_poll(struct file *fp, poll_table * wait) //fp:文件 wait: { unsigned int mask =0; poll_wait(fp, &button_wait, wait); if(even_press) //中斷事件標志, 1:退出休眠狀態 0:進入休眠狀態 mask |= POLLIN | POLLRDNORM ; return mask; //當超時,就返給應用層為0 ,被喚醒了就返回POLLIN | POLLRDNORM ; } static struct file_operations third_drv_fops={ .owner = THIS_MODULE, .open = third_drv_open, .read = third_drv_read,    .release=third_drv_class,    .poll = third_poll, //創建.poll函數 };

 

2.1 在我們1.4小節do_poll函數有一段以下代碼:

if (do_pollfd(pfd, pt))     //若返回非0,count++,后面並退出
{      
     count
++; pt = NULL; }

 

且在1.4.1分析出: do_pollfd(pfd, pt)就是指向的驅動程序third_poll()函數,

所以當我們有按鍵按下時, 驅動函數third_poll()就會返回mask非0值,然后在內核函數do_poll里的count就++,poll機制並退出睡眠.

2.2分析在內核中poll機制如何被驅動里的中斷喚醒的 

 

在驅動函數third_poll()里有以下一句:

 poll_wait(fp, &button_wait, wait);

 

 

如上圖所示,代入參數,poll_wait()就是執行了: p->qproc(filp, button_wait, p);

剛好對應了我們1.3小節的:      

table ->pt-> qproc=__pollwait;

 

所以poll_wait()函數就是調用了: __pollwait(filp, button_wait, p);

然后我們來分析__pollwait函數,pollwait的代碼如下:

static void __pollwait(struct file  *filp, wait_queue_head_t  *wait_address,poll_table  *p)
{
   ... ...
   //把current進程掛載到&entry->wait下
   init_waitqueue_entry(&entry->wait, current);

   //再&entry->wait把添加到到button_wait中斷下
   add_wait_queue(wait_address, &entry->wait);

}

 

它是將poll進程添加到了button_wait中斷隊列里,這樣,一有按鍵按下時,在中斷服務函數里就會喚醒button_wait中斷,同樣也會喚醒poll機制,使poll機制重新進程休眠計數

2.3 驅動程序.poll函數返回值介紹

當中斷休眠狀態時,返回mask為0

當運行時返回:mask |= POLLIN | POLLRDNORM

其中參數意義如下:

常量

說明

POLLIN

普通或優先級帶數據可讀

POLLRDNORM

normal普通數據可讀

POLLRDBAND

優先級帶數據可讀

POLLPRI

Priority高優先級數據可讀

POLLOUT

普通數據可寫

POLLWRNORM

normal普通數據可寫

POLLWRBAND

band優先級帶數據可寫

POLLERR

發生錯誤

POLLHUP

發生掛起

POLLNVAL

描述字不是一個打開的文件

所以POLLIN | POLLRDNORM:普通數據可讀|優先級帶數據可讀

mask就返回到應用層poll函數,

 

 

3.改進測試程序third_poll_text.c(添加poll函數)

在linux中可以通過man poll 來查看poll函數如何使用

poll函數原型如下(#include <poll.h>):

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

 

參數介紹:

1) *fds:是一個poll描述符結構體數組(可以處理多個poll),結構體pollfd如下:

  struct pollfd {
               int   fd;         /* file descriptor 文件描述符*/
               short events;     /* requested events 請求的事件*/
               short revents;    /* returned events 返回的事件(函數返回值)*/
           };

 

其中events和revents值參數如下:

常量

說明

POLLIN

普通或優先級帶數據可讀

POLLRDNORM

normal普通數據可讀

POLLRDBAND

優先級帶數據可讀

POLLPRI

Priority高優先級數據可讀

POLLOUT

普通數據可寫

POLLWRNORM

normal普通數據可寫

POLLWRBAND

band優先級帶數據可寫

POLLERR

發生錯誤

POLLHUP

發生掛起

POLLNVAL

描述字不是一個打開的文件

 

2) nfds:表示多少個poll,如果1個,就填入1

3) timeout:定時多少ms

返回值介紹:

返回值為0:表示超時或者fd文件描述符無法打開

返回值為 -1:表示錯誤

返回值為>0時 :就是以下幾個常量

常量

說明

POLLIN

普通或優先級帶數據可讀

POLLRDNORM

normal普通數據可讀

POLLRDBAND

優先級帶數據可讀

POLLPRI

Priority高優先級數據可讀

POLLOUT

普通數據可寫

POLLWRNORM

normal普通數據可寫

POLLWRBAND

band優先級帶數據可寫

POLLERR

發生錯誤

POLLHUP

發生掛起

POLLNVAL

描述字不是一個打開的文件

最終改進的測試代碼如下:

#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>                 //添加poll頭文件


/*useg:    thirdtext   */
int main(int argc,char **argv)
{
  int fd,ret;
  unsigned int val=0;
  struct pollfd fds;                          //定義poll文件描述結構體               
  fd=open("/dev/buttons",O_RDWR);          
if(fd<0) {printf("can't open!!!\n"); return -1;} fds.fd=fd; fds.events= POLLIN; //請求類型是 普通或優先級帶數據可讀 while(1) { ret=poll(&fds,1,5000) ; //一個poll, 定時5000ms,進入休眠狀態 if(ret==0) //超時 { printf("time out \r\n"); } else if(ret>0) //poll機制被喚醒,表示有數據可讀 { read(fd,&val,1); //讀取一個值 printf("key_val=0X%x\r\n",val); } } return 0; }

 效果如下:

若5S沒有數據,則打印time out

下節開始學習——使用異步通知來通知信號

 


免責聲明!

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



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