1. 程式人生 > >LDD3原始碼分析之poll分析

LDD3原始碼分析之poll分析

作者:劉昊昱

編譯環境: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函式的使用

使用者態的poll函式用以監測一組檔案描述符是否可以執行指定的I/O

操作,如果被監測的檔案描述符都不能執行指定的I/O操作,則poll函式會阻塞,直到有檔案描述符的狀態發生變化,可以執行指定的I/O操作才解除阻塞。poll函式還可以指定一個最長阻塞時間,如果時間超時,將直接返回。poll函式的函式原型如下:

[cpp] view plaincopyprint?
  1. int poll(struct pollfd *fds, nfds_t nfds, int timeout);  
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

要監測的檔案描述符由第一個引數fds指定,它是一個struct pollfd

陣列,pollfd結構體定義如下:

[cpp] view plaincopyprint?
  1. struct pollfd {  
  2. int   fd;         /* file descriptor */
  3. short events;     /* requested events */
  4. short revents;    /* returned events */
  5. };  
struct pollfd {
    int   fd;         /* file descriptor */
    short events;     /* requested events */
    short revents;    /* returned events */
};

pollfd結構體的第一個成員fd是檔案描述符,代表一個開啟的檔案。第二個成員events是一個輸入引數,用於指定poll監測哪些事件(如可讀、可寫等等)。第三個成員revents是一個輸出引數,由核心填充,指示對於檔案描述符fd,發生了哪些事件(如可讀、可寫等等)

poll函式的第二個引數nfds代表監測的檔案描述符的個數,即fds陣列的成員個數。

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

poll函式如果返回一個正數,代表核心返回了狀態(儲存在pollfd.revents)的檔案描述符的個數。如果poll返回0,表明是因為超時而返回的。如果poll返回-1,表明poll調用出錯。

poll函式可以監測哪些狀態(pollfd.events指定),以及核心可以返回哪些狀態(儲存在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操作(核心態poll)的測試程式,該程式使用了我們前面介紹的poll函式(使用者態poll)。其程式碼如下:

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

int main(int argc, char *argv[])
{
    int fd0, fd1, fd2, fd3;
    struct pollfd poll_fd[4];
    char buf[100];
    int retval;

    if(argc != 2 || ((strcmp(argv[1], "read") != 0) && (strcmp(argv[1], "write") != 0)))
    {
        printf("usage: ./poll_test read|write\n");
        return -1;
    }

    fd0 = open("/dev/scullpipe0", O_RDWR);
    if ( fd0 < 0)
    {
        printf("open scullpipe0 error\n");
        return -1;
    }
    
    fd1 = open("/dev/scullpipe1", O_RDWR);
    if ( fd1 < 0)
    {
        printf("open scullpipe1 error\n");
        return -1;
    }
    
    fd2 = open("/dev/scullpipe2", O_RDWR);
    if ( fd2 < 0)
    {
        printf("open scullpipe2 error\n");
        return -1;
    }
    
    fd3 = open("/dev/scullpipe3", O_RDWR);
    if ( fd3 < 0)
    {
        printf("open scullpipe3 error\n");
        return -1;
    }

    if(strcmp(argv[1], "read") == 0)
    {
        poll_fd[0].fd = fd0;
        poll_fd[1].fd = fd1;
        poll_fd[2].fd = fd2;
        poll_fd[3].fd = fd3;

        poll_fd[0].events = POLLIN | POLLRDNORM;
        poll_fd[1].events = POLLIN | POLLRDNORM;
        poll_fd[2].events = POLLIN | POLLRDNORM;
        poll_fd[3].events = POLLIN | POLLRDNORM;

        retval = poll(poll_fd, 4, 10000);
    }
    else
    {
        poll_fd[0].fd = fd0;
        poll_fd[1].fd = fd1;
        poll_fd[2].fd = fd2;
        poll_fd[3].fd = fd3;

        poll_fd[0].events = POLLOUT | POLLWRNORM;
        poll_fd[1].events = POLLOUT | POLLWRNORM;
        poll_fd[2].events = POLLOUT | POLLWRNORM;
        poll_fd[3].events = POLLOUT | POLLWRNORM;
        
        retval = poll(poll_fd, 4, 10000);
    }

    if (retval == -1)
    {
        printf("poll error!\n");
        return -1;
    }
    else if (retval)
    {
        if(strcmp(argv[1], "read") == 0)
        {
            if(poll_fd[0].revents & (POLLIN | POLLRDNORM))
            {
                printf("/dev/scullpipe0 is readable!\n");
                memset(buf, 0, 100);
                read(fd0, buf, 100);
                printf("%s\n", buf);
            }

            if(poll_fd[1].revents & (POLLIN | POLLRDNORM))
            {
                printf("/dev/scullpipe1 is readable!\n");
                memset(buf, 0, 100);
                read(fd1, buf, 100);
                printf("%s\n", buf);
            }

            if(poll_fd[2].revents & (POLLIN | POLLRDNORM))
            {
                printf("/dev/scullpipe2 is readable!\n");
                memset(buf, 0, 100);
                read(fd2, buf, 100);
                printf("%s\n", buf);
            }

            if(poll_fd[3].revents & (POLLIN | POLLRDNORM))
            {
                printf("/dev/scullpipe3 is readable!\n");
                memset(buf, 0, 100);
                read(fd3, buf, 100);
                printf("%s\n", buf);
            }
        }
        else
        {
            if(poll_fd[0].revents & (POLLOUT | POLLWRNORM))
            {
                printf("/dev/scullpipe0 is writable!\n");
            }

            if(poll_fd[1].revents & (POLLOUT | POLLWRNORM))
            {
                printf("/dev/scullpipe1 is writable!\n");
            }

            if(poll_fd[2].revents & (POLLOUT | POLLWRNORM))
            {
                printf("/dev/scullpipe2 is writable!\n");
            }

            if(poll_fd[3].revents & (POLLOUT | POLLWRNORM))
            {
                printf("/dev/scullpipe3 is writable!\n");
            }
        }
    }
    else
    {
        if(strcmp(argv[1], "read") == 0)
        {
            printf("No data within ten seconds.\n");
        }
        else
        {
            printf("Can not write within ten seconds.\n");
        }
    }

    return 0;
}

測試過程如下圖所示:

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

下面再次測試讀操作,因為裝置中沒有內容,還是阻塞住,但是在10秒鐘內,從另外一個終端向/dev/scullpipe2寫入資料,這裡是寫入字串”hello”,可以看到,寫入資料後,測試程式解除阻塞,並列印”/dev/scullpipe2 is readable!””hello”字串,這個過程如下圖所示:

二、select函式的使用

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

select函式的函式原型如下:

[cpp] view plaincopyprint?
  1. int select(int nfds, fd_set *readfds, fd_set *writefds,  
  2.                   fd_set *exceptfds, struct timeval *timeout);  
int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

select通過三個獨立的檔案描述符集(fd_set)readfdswritefdsexceptfds指示監測哪些檔案描述符,其中readfds中的檔案描述符監測是否可進行非阻塞的讀操作。writefds陣列中的檔案描述符監測是否可進行非阻塞的寫操作。exceptfds陣列中的檔案描述符監測是否有異常(exceptions)

select的第一個引數nfds是三個檔案描述符集readfdswritefdsexceptfds中最大檔案描述符值加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操作的測試程式,其程式碼如下:

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

int main(int argc, char *argv[])
{
    fd_set rfds, wfds;
    int fd0, fd1, fd2, fd3;
    char buf[100];
    int retval;    

    /* Wait up to ten seconds. */
    struct timeval tv;
    tv.tv_sec = 10;
    tv.tv_usec = 0;

    if(argc != 2 || ((strcmp(argv[1], "read") != 0) && (strcmp(argv[1], "write") != 0)))
    {
        printf("usage: ./select_test read|write\n");
        return -1;
    }

    fd0 = open("/dev/scullpipe0", O_RDWR);
    if ( fd0 < 0)
    {
        printf("open scullpipe0 error\n");
        return -1;
    }
    
    fd1 = open("/dev/scullpipe1", O_RDWR);
    if ( fd1 < 0)
    {
        printf("open scullpipe1 error\n");
        return -1;
    }
    
    fd2 = open("/dev/scullpipe2", O_RDWR);
    if ( fd2 < 0)
    {
        printf("open scullpipe2 error\n");
        return -1;
    }
    
    fd3 = open("/dev/scullpipe3", O_RDWR);
    if ( fd3 < 0)
    {
        printf("open scullpipe3 error\n");
        return -1;
    }

    if(strcmp(argv[1], "read") == 0)
    {
        FD_ZERO(&rfds);
        FD_SET(fd0, &rfds);
        FD_SET(fd1, &rfds);
        FD_SET(fd2, &rfds);
        FD_SET(fd3, &rfds);
        retval = select(fd3 + 1, &rfds, NULL, NULL, &tv);
    }
    else
    {
        FD_ZERO(&wfds);
        FD_SET(fd0, &wfds);
        FD_SET(fd1, &wfds);
        FD_SET(fd2, &wfds);
        FD_SET(fd3, &wfds);
        retval = select(fd3 + 1, NULL, &wfds, NULL, &tv);
    }

    if (retval == -1)
    {
        printf("select error!\n");
        return -1;
    }
    else if (retval)
    {
        if(strcmp(argv[1], "read") == 0)
        {
            if(FD_ISSET(fd0, &rfds))
            {
                printf("/dev/scullpipe0 is readable!\n");
                memset(buf, 0, 100);
                read(fd0, buf, 100);
                printf("%s\n", buf);
            }

            if(FD_ISSET(fd1, &rfds))
            {
                printf("/dev/scullpipe1 is readable!\n");
                memset(buf, 0, 100);
                read(fd1, buf, 100);
                printf("%s\n", buf);
            }

            if(FD_ISSET(fd2, &rfds))
            {
                printf("/dev/scullpipe2 is readable!\n");
                memset(buf, 0, 100);
                read(fd2, buf, 100);
                printf("%s\n", buf);
            }

            if(FD_ISSET(fd3, &rfds))
            {
                printf("/dev/scullpipe3 is readable!\n");
                memset(buf, 0, 100);
                read(fd3, buf, 100);
                printf("%s\n", buf);
            }
        }
        else
        {
            if(FD_ISSET(fd0, &wfds))
            {
                printf("/dev/scullpipe0 is writable!\n");
            }

            if(FD_ISSET(fd1, &wfds))
            {
                printf("/dev/scullpipe1 is writable!\n");
            }

            if(FD_ISSET(fd2, &wfds))
            {
                printf("/dev/scullpipe2 is writable!\n");
            }

            if(FD_ISSET(fd3, &wfds))
            {
                printf("/dev/scullpipe3 is writable!\n");
            }
        }
    }
    else
    {
        if(strcmp(argv[1], "read") == 0)
        {
            printf("No data within ten seconds.\n");
        }
        else
        {
            printf("Can not write within ten seconds.\n");
        }
    }

    return 0;
}

測試過程如下圖所示:

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

下面再次測試讀操作,因為裝置中沒有內容,還是阻塞住,但是在10秒鐘內,從另外一個終端向/dev/scullpipe2寫入資料,這裡是寫入字串”hello”,可以看到,寫入資料後,測試程式解除阻塞,並列印”/dev/scullpipe2 is readable!””hello”字串,這個過程如下圖所示:

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

使用者空間的pollselect函式,最終都會呼叫驅動程式中的poll函式,其函式原型如下:

[cpp] view plaincopyprint?
  1. unsigned int (*poll) (struct file *filp, poll_table *wait);  
unsigned int (*poll) (struct file *filp, poll_table *wait);

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

一是把能標誌輪詢狀態變化的等待佇列加入到poll_table中,這通過呼叫poll_wait函式實現。

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

poll函式的第二個引數poll_table,是核心中用來實現pollselect系統呼叫的結構體,對於驅動開發者來說,不必關心其具體內容,可以把poll_table看成是不透明的結構體,只要拿過來使用就可以了。驅動程式通過poll_wait函式,把能夠喚醒程序,改變輪詢狀態的等待佇列加入到poll_table中。該函式定義如下:

[cpp] view plaincopyprint?
  1. void poll_wait (struct file *, wait_queue_head_t *, poll_table *);  
void poll_wait (struct file *, wait_queue_head_t *, poll_table *);

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

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

[cpp] view plaincopyprint?
  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}  
228static unsigned int scull_p_poll(struct file *filp, poll_table *wait)
229{
230    struct scull_pipe *dev = filp->private_data;
231    unsigned int mask = 0;
232
233    /*
234     * The buffer is circular; it is considered full
235     * if "wp" is right behind "rp" and empty if the
236     * two are equal.
237     */
238    down(&dev->sem);
239    poll_wait(filp, &dev->inq,  wait);
240    poll_wait(filp, &dev->outq, wait);
241    if (dev->rp != dev->wp)
242        mask |= POLLIN | POLLRDNORM;    /* readable */
243    if (spacefree(dev))
244        mask |= POLLOUT | POLLWRNORM;   /* writable */
245    up(&dev->sem);
246    return mask;
247}

239行,呼叫poll_wait函式將讀等待佇列加入到poll_table中。

240行,呼叫poll_wait函式將寫等待佇列加入到poll_table中。

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

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

246行,將標誌位返回。

驅動程式中的poll函式很簡單,那麼核心是怎麼實現pollselect系統呼叫的呢?當用戶空間程式呼叫pollselect函式時,核心會呼叫由使用者程式指定的全部檔案的poll方法,並向它們傳遞同一個poll_table結構。poll_table結構其實是一個生成實際資料結構的函式(名為poll_queue_proc)的封裝,這個函式poll_queue_proc,不同的應用場景,核心有不同的實現,這裡我們不仔細研究對應的函式。對於pollselect系統呼叫來說,這個實際資料結構是一個包含poll_table_entry結構的記憶體頁連結串列。這裡提到的相關資料結構在linux-2.6.32-38原始碼中,定義在include/linux/poll.h檔案中,程式碼如下所示:

[cpp] view plaincopyprint?
  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 inlinevoid 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 inlinevoid 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};  
 30/*
 31 * structures and helpers for f_op->poll implementations
 32 */
 33typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);
 34
 35typedef struct poll_table_struct {
 36    poll_queue_proc qproc;
 37    unsigned long key;
 38} poll_table;
 39
 40static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
 41{
 42    if (p && wait_address)
 43        p->qproc(filp, wait_address, p);
 44}
 45
 46static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
 47{
 48    pt->qproc = qproc;
 49    pt->key   = ~0UL; /* all events enabled */
 50}
 51
 52struct poll_table_entry {
 53    struct file *filp;
 54    unsigned long key;
 55    wait_queue_t wait;
 56    wait_queue_head_t *wait_address;
 57};

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