一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

LDD3源碼分析之poll分析

 WUCANADA 2012-06-05

LDD3源碼分析之poll分析

分類: LDD3源碼分析 103人閱讀 評論(0) 收藏 舉報

作者:劉昊昱 

博客:http://blog.csdn.net/liuhaoyutz

編譯環(huán)境:Ubuntu 10.10

內核版本:2.6.32-38-generic-pae

LDD3源碼路徑:examples/scull/pipe.c  examples/scull/main.c

 

本文分析LDD36章的poll(輪詢)操作。要理解驅動程序中poll函數的作用和實現,必須先理解用戶空間中pollselect函數的用法。本文與前面的文章介紹的順序有所不同,首先分析測試程序,以此理解用戶空間中的pollselect函數的用法。然后再分析驅動程序怎樣對用戶空間的pollselect函數提供支持。

 

一、poll函數的使用

用戶態(tài)的poll函數用以監(jiān)測一組文件描述符是否可以執(zhí)行指定的I/O操作,如果被監(jiān)測的文件描述符都不能執(zhí)行指定的I/O操作,則poll函數會阻塞,直到有文件描述符的狀態(tài)發(fā)生變化,可以執(zhí)行指定的I/O操作才解除阻塞。poll函數還可以指定一個最長阻塞時間,如果時間超時,將直接返回。poll函數的函數原型如下:

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

要監(jiān)測的文件描述符由第一個參數fds指定,它是一個struct pollfd數組,pollfd結構體定義如下:

  1. struct pollfd {  
  2.     int   fd;         /* file descriptor */  
  3.     short events;     /* requested events */  
  4.     short revents;    /* returned events */  
  5. };  

pollfd結構體的第一個成員fd是文件描述符,代表一個打開的文件。第二個成員events是一個輸入參數,用于指定poll監(jiān)測哪些事件(如可讀、可寫等等)。第三個成員revents是一個輸出參數,由內核填充,指示對于文件描述符fd,發(fā)生了哪些事件(如可讀、可寫等等)。

poll函數的第二個參數nfds代表監(jiān)測的文件描述符的個數,即fds數組的成員個數。

poll函數的第三個參數timeout代表阻塞時間(以毫秒為單位),如果poll要求監(jiān)測的事件沒有發(fā)生,則poll會阻塞最多timeout毫秒。如果timeout設置為負數,則poll會一直阻塞,直到監(jiān)測的事件發(fā)生。

poll函數如果返回一個正數,代表內核返回了狀態(tài)(保存在pollfd.revents)的文件描述符的個數。如果poll返回0,表明是因為超時而返回的。如果poll返回-1,表明poll調用出錯。

poll函數可以監(jiān)測哪些狀態(tài)(pollfd.events指定),以及內核可以返回哪些狀態(tài)(保存在pollfd.revents),由下面的宏設定:

POLLINThere is data to read.

POLLOUTWriting now will not block.

POLLPRIThere is urgent data to read (e.g., out-of-band data on TCP socket; pseudo-terminal master in packet mode has seen state change in slave).

POLLRDHUP (since Linux 2.6.17) Stream socket peer closed connection, or shut down writing half of connection.  The _GNU_SOURCE feature test macro must be defined in order  to  obtain this definition.

POLLERRError condition (output only).

POLLHUPHang up (output only).

POLLNVALInvalid request: fd not open (output only).

When compiling with _XOPEN_SOURCE defined, one also has the following, which convey no further information beyond the bits listed above:

POLLRDNORMEquivalent to POLLIN.

POLLRDBANDPriority band data can be read (generally unused on Linux).

POLLWRNORMEquivalent to POLLOUT.

POLLWRBANDPriority data may be written.

下面我們看一個測試scullpipe設備的poll操作(內核態(tài)poll)的測試程序,該程序使用了我們前面介紹的poll函數(用戶態(tài)poll)。其代碼如下:

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <fcntl.h>  
  5. #include <unistd.h>  
  6. #include <linux/poll.h>  
  7. #include <sys/time.h>  
  8. #include <sys/types.h>  
  9. #include <sys/stat.h>  
  10.   
  11. int main(int argc, char *argv[])  
  12. {  
  13.     int fd0, fd1, fd2, fd3;  
  14.     struct pollfd poll_fd[4];  
  15.     char buf[100];  
  16.     int retval;  
  17.   
  18.     if(argc != 2 || ((strcmp(argv[1], "read") != 0) && (strcmp(argv[1], "write") != 0)))  
  19.     {  
  20.         printf("usage: ./poll_test read|write\n");  
  21.         return -1;  
  22.     }  
  23.   
  24.     fd0 = open("/dev/scullpipe0", O_RDWR);  
  25.     if ( fd0 < 0)  
  26.     {  
  27.         printf("open scullpipe0 error\n");  
  28.         return -1;  
  29.     }  
  30.       
  31.     fd1 = open("/dev/scullpipe1", O_RDWR);  
  32.     if ( fd1 < 0)  
  33.     {  
  34.         printf("open scullpipe1 error\n");  
  35.         return -1;  
  36.     }  
  37.       
  38.     fd2 = open("/dev/scullpipe2", O_RDWR);  
  39.     if ( fd2 < 0)  
  40.     {  
  41.         printf("open scullpipe2 error\n");  
  42.         return -1;  
  43.     }  
  44.       
  45.     fd3 = open("/dev/scullpipe3", O_RDWR);  
  46.     if ( fd3 < 0)  
  47.     {  
  48.         printf("open scullpipe3 error\n");  
  49.         return -1;  
  50.     }  
  51.   
  52.     if(strcmp(argv[1], "read") == 0)  
  53.     {  
  54.         poll_fd[0].fd = fd0;  
  55.         poll_fd[1].fd = fd1;  
  56.         poll_fd[2].fd = fd2;  
  57.         poll_fd[3].fd = fd3;  
  58.   
  59.         poll_fd[0].events = POLLIN | POLLRDNORM;  
  60.         poll_fd[1].events = POLLIN | POLLRDNORM;  
  61.         poll_fd[2].events = POLLIN | POLLRDNORM;  
  62.         poll_fd[3].events = POLLIN | POLLRDNORM;  
  63.   
  64.         retval = poll(poll_fd, 4, 10000);  
  65.     }  
  66.     else  
  67.     {  
  68.         poll_fd[0].fd = fd0;  
  69.         poll_fd[1].fd = fd1;  
  70.         poll_fd[2].fd = fd2;  
  71.         poll_fd[3].fd = fd3;  
  72.   
  73.         poll_fd[0].events = POLLOUT | POLLWRNORM;  
  74.         poll_fd[1].events = POLLOUT | POLLWRNORM;  
  75.         poll_fd[2].events = POLLOUT | POLLWRNORM;  
  76.         poll_fd[3].events = POLLOUT | POLLWRNORM;  
  77.           
  78.         retval = poll(poll_fd, 4, 10000);  
  79.     }  
  80.   
  81.     if (retval == -1)  
  82.     {  
  83.         printf("poll error!\n");  
  84.         return -1;  
  85.     }  
  86.     else if (retval)  
  87.     {  
  88.         if(strcmp(argv[1], "read") == 0)  
  89.         {  
  90.             if(poll_fd[0].revents & (POLLIN | POLLRDNORM))  
  91.             {  
  92.                 printf("/dev/scullpipe0 is readable!\n");  
  93.                 memset(buf, 0, 100);  
  94.                 read(fd0, buf, 100);  
  95.                 printf("%s\n", buf);  
  96.             }  
  97.   
  98.             if(poll_fd[1].revents & (POLLIN | POLLRDNORM))  
  99.             {  
  100.                 printf("/dev/scullpipe1 is readable!\n");  
  101.                 memset(buf, 0, 100);  
  102.                 read(fd1, buf, 100);  
  103.                 printf("%s\n", buf);  
  104.             }  
  105.   
  106.             if(poll_fd[2].revents & (POLLIN | POLLRDNORM))  
  107.             {  
  108.                 printf("/dev/scullpipe2 is readable!\n");  
  109.                 memset(buf, 0, 100);  
  110.                 read(fd2, buf, 100);  
  111.                 printf("%s\n", buf);  
  112.             }  
  113.   
  114.             if(poll_fd[3].revents & (POLLIN | POLLRDNORM))  
  115.             {  
  116.                 printf("/dev/scullpipe3 is readable!\n");  
  117.                 memset(buf, 0, 100);  
  118.                 read(fd3, buf, 100);  
  119.                 printf("%s\n", buf);  
  120.             }  
  121.         }  
  122.         else  
  123.         {  
  124.             if(poll_fd[0].revents & (POLLOUT | POLLWRNORM))  
  125.             {  
  126.                 printf("/dev/scullpipe0 is writable!\n");  
  127.             }  
  128.   
  129.             if(poll_fd[1].revents & (POLLOUT | POLLWRNORM))  
  130.             {  
  131.                 printf("/dev/scullpipe1 is writable!\n");  
  132.             }  
  133.   
  134.             if(poll_fd[2].revents & (POLLOUT | POLLWRNORM))  
  135.             {  
  136.                 printf("/dev/scullpipe2 is writable!\n");  
  137.             }  
  138.   
  139.             if(poll_fd[3].revents & (POLLOUT | POLLWRNORM))  
  140.             {  
  141.                 printf("/dev/scullpipe3 is writable!\n");  
  142.             }  
  143.         }  
  144.     }  
  145.     else  
  146.     {  
  147.         if(strcmp(argv[1], "read") == 0)  
  148.         {  
  149.             printf("No data within ten seconds.\n");  
  150.         }  
  151.         else  
  152.         {  
  153.             printf("Can not write within ten seconds.\n");  
  154.         }  
  155.     }  
  156.   
  157.     return 0;  
  158. }  

測試過程如下圖所示:

從上圖可以看出,scullpipe0 - scullpipe3都是可寫的。但是因為沒有向其中寫入任何內容,所以讀的時候會阻塞住,因為測試程序設置最長阻塞時間為10秒,所以10秒后解除阻塞退出,并打印”No data within ten seconds.”。

下面再次測試讀操作,因為設備中沒有內容,還是阻塞住,但是在10秒鐘內,從另外一個終端向/dev/scullpipe2寫入數據,這里是寫入字符串”hello”,可以看到,寫入數據后,測試程序解除阻塞,并打印”/dev/scullpipe2 is readable!””hello”字符串,這個過程如下圖所示:

 

二、select函數的使用

select函數的作用與poll函數類似,也是監(jiān)測一組文件描述符是否準備好執(zhí)行指定的I/O操作。如果沒有任何一個文件描述符可以完成指定的操作,select函數會阻塞住。select函數可以指定一個最長阻塞時間,如果超時,則直接返回。

select函數的函數原型如下:

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

select通過三個獨立的文件描述符集(fd_set)readfds,writefdsexceptfds指示監(jiān)測哪些文件描述符,其中readfds中的文件描述符監(jiān)測是否可進行非阻塞的讀操作。writefds數組中的文件描述符監(jiān)測是否可進行非阻塞的寫操作。exceptfds數組中的文件描述符監(jiān)測是否有異常(exceptions)

select的第一個參數nfds是三個文件描述符集readfds、writefds、exceptfds中最大文件描述符值加1。

select的最后一個參數timeout代表最長阻塞時間。

select函數返回滿足指定I/O要求的文件描述符的個數,如果是超時退出的,select函數返回0。如果出錯,select函數返回-1。

有四個宏用來操作文件描述符集:

void FD_ZERO(fd_set *set);  清空一個文件描述符集。

void FD_SET(int fd, fd_set *set); 將一個文件描述符fd加入到指定的文件描述符集set中。

void FD_CLR(int fd, fd_set *set); 將一個文件描述符fd從指定的文件描述符集set中刪除。

int  FD_ISSET(int fd, fd_set *set); 測試文件描述符fd是否在指定的文件描述符集set中。

下面我們看使用select函數實現的測試驅動中poll操作的測試程序,其代碼如下:

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <fcntl.h>  
  5. #include <unistd.h>  
  6. #include <sys/time.h>  
  7. #include <sys/types.h>  
  8. #include <sys/stat.h>  
  9.   
  10. int main(int argc, char *argv[])  
  11. {  
  12.     fd_set rfds, wfds;  
  13.     int fd0, fd1, fd2, fd3;  
  14.     char buf[100];  
  15.     int retval;      
  16.   
  17.     /* Wait up to ten seconds. */  
  18.     struct timeval tv;  
  19.     tv.tv_sec = 10;  
  20.     tv.tv_usec = 0;  
  21.   
  22.     if(argc != 2 || ((strcmp(argv[1], "read") != 0) && (strcmp(argv[1], "write") != 0)))  
  23.     {  
  24.         printf("usage: ./select_test read|write\n");  
  25.         return -1;  
  26.     }  
  27.   
  28.     fd0 = open("/dev/scullpipe0", O_RDWR);  
  29.     if ( fd0 < 0)  
  30.     {  
  31.         printf("open scullpipe0 error\n");  
  32.         return -1;  
  33.     }  
  34.       
  35.     fd1 = open("/dev/scullpipe1", O_RDWR);  
  36.     if ( fd1 < 0)  
  37.     {  
  38.         printf("open scullpipe1 error\n");  
  39.         return -1;  
  40.     }  
  41.       
  42.     fd2 = open("/dev/scullpipe2", O_RDWR);  
  43.     if ( fd2 < 0)  
  44.     {  
  45.         printf("open scullpipe2 error\n");  
  46.         return -1;  
  47.     }  
  48.       
  49.     fd3 = open("/dev/scullpipe3", O_RDWR);  
  50.     if ( fd3 < 0)  
  51.     {  
  52.         printf("open scullpipe3 error\n");  
  53.         return -1;  
  54.     }  
  55.   
  56.     if(strcmp(argv[1], "read") == 0)  
  57.     {  
  58.         FD_ZERO(&rfds);  
  59.         FD_SET(fd0, &rfds);  
  60.         FD_SET(fd1, &rfds);  
  61.         FD_SET(fd2, &rfds);  
  62.         FD_SET(fd3, &rfds);  
  63.         retval = select(fd3 + 1, &rfds, NULL, NULL, &tv);  
  64.     }  
  65.     else  
  66.     {  
  67.         FD_ZERO(&wfds);  
  68.         FD_SET(fd0, &wfds);  
  69.         FD_SET(fd1, &wfds);  
  70.         FD_SET(fd2, &wfds);  
  71.         FD_SET(fd3, &wfds);  
  72.         retval = select(fd3 + 1, NULL, &wfds, NULL, &tv);  
  73.     }  
  74.   
  75.     if (retval == -1)  
  76.     {  
  77.         printf("select error!\n");  
  78.         return -1;  
  79.     }  
  80.     else if (retval)  
  81.     {  
  82.         if(strcmp(argv[1], "read") == 0)  
  83.         {  
  84.             if(FD_ISSET(fd0, &rfds))  
  85.             {  
  86.                 printf("/dev/scullpipe0 is readable!\n");  
  87.                 memset(buf, 0, 100);  
  88.                 read(fd0, buf, 100);  
  89.                 printf("%s\n", buf);  
  90.             }  
  91.   
  92.             if(FD_ISSET(fd1, &rfds))  
  93.             {  
  94.                 printf("/dev/scullpipe1 is readable!\n");  
  95.                 memset(buf, 0, 100);  
  96.                 read(fd1, buf, 100);  
  97.                 printf("%s\n", buf);  
  98.             }  
  99.   
  100.             if(FD_ISSET(fd2, &rfds))  
  101.             {  
  102.                 printf("/dev/scullpipe2 is readable!\n");  
  103.                 memset(buf, 0, 100);  
  104.                 read(fd2, buf, 100);  
  105.                 printf("%s\n", buf);  
  106.             }  
  107.   
  108.             if(FD_ISSET(fd3, &rfds))  
  109.             {  
  110.                 printf("/dev/scullpipe3 is readable!\n");  
  111.                 memset(buf, 0, 100);  
  112.                 read(fd3, buf, 100);  
  113.                 printf("%s\n", buf);  
  114.             }  
  115.         }  
  116.         else  
  117.         {  
  118.             if(FD_ISSET(fd0, &wfds))  
  119.             {  
  120.                 printf("/dev/scullpipe0 is writable!\n");  
  121.             }  
  122.   
  123.             if(FD_ISSET(fd1, &wfds))  
  124.             {  
  125.                 printf("/dev/scullpipe1 is writable!\n");  
  126.             }  
  127.   
  128.             if(FD_ISSET(fd2, &wfds))  
  129.             {  
  130.                 printf("/dev/scullpipe2 is writable!\n");  
  131.             }  
  132.   
  133.             if(FD_ISSET(fd3, &wfds))  
  134.             {  
  135.                 printf("/dev/scullpipe3 is writable!\n");  
  136.             }  
  137.         }  
  138.     }  
  139.     else  
  140.     {  
  141.         if(strcmp(argv[1], "read") == 0)  
  142.         {  
  143.             printf("No data within ten seconds.\n");  
  144.         }  
  145.         else  
  146.         {  
  147.             printf("Can not write within ten seconds.\n");  
  148.         }  
  149.     }  
  150.   
  151.     return 0;  
  152. }  

測試過程如下圖所示:

從上圖可以看出,scullpipe0 - scullpipe3都是可寫的。但是因為沒有向其中寫入任何內容,所以讀的時候會阻塞住,因為測試程序設置最長阻塞時間為10秒,所以10秒后解除阻塞退出,并打印”No data within ten seconds.”。

下面再次測試讀操作,因為設備中沒有內容,還是阻塞住,但是在10秒鐘內,從另外一個終端向/dev/scullpipe2寫入數據,這里是寫入字符串”hello”,可以看到,寫入數據后,測試程序解除阻塞,并打印”/dev/scullpipe2 is readable!””hello”字符串,這個過程如下圖所示:

 

三、驅動程序中poll操作的實現

用戶空間的pollselect函數,最終都會調用驅動程序中的poll函數,其函數原型如下:

  1. unsigned int (*poll) (struct file *filp, poll_table *wait);  

poll函數應該實現兩個功能:

一是把能標志輪詢狀態(tài)變化的等待隊列加入到poll_table中,這通過調用poll_wait函數實現。

二是返回指示能進行的I/O操作的標志位。

poll函數的第二個參數poll_table,是內核中用來實現poll,select系統(tǒng)調用的結構體,對于驅動開發(fā)者來說,不必關心其具體內容,可以把poll_table看成是不透明的結構體,只要拿過來使用就可以了。驅動程序通過poll_wait函數,把能夠喚醒進程,改變輪詢狀態(tài)的等待隊列加入到poll_table中。該函數定義如下:

  1. void poll_wait (struct file *, wait_queue_head_t *, poll_table *);  

對于poll函數的第二個功能,返回的標志位與用戶空間相對應,最常用的標志位是POLLIN | POLLRDNORMPOLLOUT | POLLWRNORM,分別標志可進行非阻塞的讀和寫操作。

下面看scullpipe設備對poll操作的實現,其內容其實非常簡單:

  1. 228static unsigned int scull_p_poll(struct file *filp, poll_table *wait)  
  2. 229{  
  3. 230    struct scull_pipe *dev = filp->private_data;  
  4. 231    unsigned int mask = 0;  
  5. 232  
  6. 233    /* 
  7. 234     * The buffer is circular; it is considered full 
  8. 235     * if "wp" is right behind "rp" and empty if the 
  9. 236     * two are equal. 
  10. 237     */  
  11. 238    down(&dev->sem);  
  12. 239    poll_wait(filp, &dev->inq,  wait);  
  13. 240    poll_wait(filp, &dev->outq, wait);  
  14. 241    if (dev->rp != dev->wp)  
  15. 242        mask |= POLLIN | POLLRDNORM;    /* readable */  
  16. 243    if (spacefree(dev))  
  17. 244        mask |= POLLOUT | POLLWRNORM;   /* writable */  
  18. 245    up(&dev->sem);  
  19. 246    return mask;  
  20. 247}  

239行,調用poll_wait函數將讀等待隊列加入到poll_table中。

240行,調用poll_wait函數將寫等待隊列加入到poll_table中。

241 - 242行,如果有內容可讀,設置可讀標志位。

243 - 244行,如果有空間可寫,設置可寫標志位。

246行,將標志位返回。

驅動程序中的poll函數很簡單,那么內核是怎么實現pollselect系統(tǒng)調用的呢?當用戶空間程序調用pollselect函數時,內核會調用由用戶程序指定的全部文件的poll方法,并向它們傳遞同一個poll_table結構。poll_table結構其實是一個生成實際數據結構的函數(名為poll_queue_proc)的封裝,這個函數poll_queue_proc,不同的應用場景,內核有不同的實現,這里我們不仔細研究對應的函數。對于pollselect系統(tǒng)調用來說,這個實際數據結構是一個包含poll_table_entry結構的內存頁鏈表。這里提到的相關數據結構在linux-2.6.32-38源碼中,定義在include/linux/poll.h文件中,代碼如下所示:

  1. 30/* 
  2. 31 * structures and helpers for f_op->poll implementations 
  3. 32 */  
  4. 33typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);  
  5. 34  
  6. 35typedef struct poll_table_struct {  
  7. 36    poll_queue_proc qproc;  
  8. 37    unsigned long key;  
  9. 38} poll_table;  
  10. 39  
  11. 40static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)  
  12. 41{  
  13. 42    if (p && wait_address)  
  14. 43        p->qproc(filp, wait_address, p);  
  15. 44}  
  16. 45  
  17. 46static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)  
  18. 47{  
  19. 48    pt->qproc = qproc;  
  20. 49    pt->key   = ~0UL; /* all events enabled */  
  21. 50}  
  22. 51  
  23. 52struct poll_table_entry {  
  24. 53    struct file *filp;  
  25. 54    unsigned long key;  
  26. 55    wait_queue_t wait;  
  27. 56    wait_queue_head_t *wait_address;  
  28. 57};  

poll操作我們就分析完了,內核要求驅動程序做的事并不多,但是我們在學習時,要把poll操作和前面介紹的阻塞型read/write函數以及scullpipe設備的等待隊列等結合起來考慮,因為scullpipe設備是一個完整的程序模塊。

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    午夜精品一区二区av| 千仞雪下面好爽好紧好湿全文| 国产精品二区三区免费播放心| 九九热在线视频观看最新| 欧美日韩久久精品一区二区| 激情中文字幕在线观看| 国产精品午夜小视频观看| 欧美日韩精品久久亚洲区熟妇人| 欧美日韩亚洲精品内裤| 亚洲国产精品久久网午夜| 国产又粗又黄又爽又硬的| 又黄又色又爽又免费的视频| 少妇熟女精品一区二区三区| 精品一区二区三区人妻视频| 女生更色还是男生更色| 日韩欧美二区中文字幕| 中文字幕禁断介一区二区| 日本熟妇五十一区二区三区| 亚洲欧洲在线一区二区三区| 国产一区欧美午夜福利| 少妇熟女亚洲色图av天堂| 日本在线视频播放91| 日韩国产亚洲欧美另类| 亚洲国产精品一区二区毛片| 91欧美视频在线观看免费| 99久久精品久久免费| 不卡在线播放一区二区三区| 91日韩欧美中文字幕| 99久久精品午夜一区| 激情爱爱一区二区三区| 九九热最新视频免费观看| 东京热男人的天堂一二三区| 中文字幕有码视频熟女| 日本高清视频在线播放| 又色又爽又无遮挡的视频| 91亚洲人人在字幕国产 | 日韩免费av一区二区三区| 国产伦精品一区二区三区精品视频 | 中文字幕日韩精品人一妻| 亚洲另类欧美综合日韩精品 | 激情中文字幕在线观看|