LDD3原始碼分析之poll分析
作者:劉昊昱
編譯環境:Ubuntu 10.10
核心版本:2.6.32-38-generic-pae
LDD3原始碼路徑:examples/scull/pipe.c examples/scull/main.c
本文分析LDD3第6章的poll(輪詢)操作。要理解驅動程式中poll函式的作用和實現,必須先理解使用者空間中poll和select函式的用法。本文與前面的文章介紹的順序有所不同,首先分析測試程式,以此理解使用者空間中的poll和select函式的用法。然後再分析驅動程式怎樣對使用者空間的poll和select函式提供支援。
一、poll函式的使用
使用者態的poll函式用以監測一組檔案描述符是否可以執行指定的I/O
- int poll(struct pollfd *fds, nfds_t nfds, int timeout);
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
要監測的檔案描述符由第一個引數fds指定,它是一個struct pollfd
- struct pollfd {
- int fd; /* file descriptor */
- short events; /* requested events */
- short revents; /* returned events */
- };
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中),由下面的巨集設定:
POLLIN:There is data to read.
POLLOUT:Writing now will not block.
POLLPRI:There 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.
POLLERR:Error condition (output only).
POLLHUP:Hang up (output only).
POLLNVAL:Invalid 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:
POLLRDNORM:Equivalent to POLLIN.
POLLRDBAND:Priority band data can be read (generally unused on Linux).
POLLWRNORM:Equivalent to POLLOUT.
POLLWRBAND:Priority data may be written.
下面我們看一個測試scullpipe裝置的poll操作(核心態poll)的測試程式,該程式使用了我們前面介紹的poll函式(使用者態poll)。其程式碼如下:
[cpp] view plaincopyprint?- #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;
- }
- elseif (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;
- }
#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?- int select(int nfds, fd_set *readfds, fd_set *writefds,
- 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)readfds,writefds,exceptfds指示監測哪些檔案描述符,其中readfds中的檔案描述符監測是否可進行非阻塞的讀操作。writefds陣列中的檔案描述符監測是否可進行非阻塞的寫操作。exceptfds陣列中的檔案描述符監測是否有異常(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操作的測試程式,其程式碼如下:
[cpp] view plaincopyprint?- #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;
- }
- elseif (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;
- }
#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操作的實現
使用者空間的poll和select函式,最終都會呼叫驅動程式中的poll函式,其函式原型如下:
[cpp] view plaincopyprint?- 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,是核心中用來實現poll,select系統呼叫的結構體,對於驅動開發者來說,不必關心其具體內容,可以把poll_table看成是不透明的結構體,只要拿過來使用就可以了。驅動程式通過poll_wait函式,把能夠喚醒程序,改變輪詢狀態的等待佇列加入到poll_table中。該函式定義如下:
[cpp] view plaincopyprint?- void poll_wait (struct file *, wait_queue_head_t *, poll_table *);
void poll_wait (struct file *, wait_queue_head_t *, poll_table *);
對於poll函式的第二個功能,返回的標誌位與使用者空間相對應,最常用的標誌位是POLLIN | POLLRDNORM和POLLOUT | POLLWRNORM,分別標誌可進行非阻塞的讀和寫操作。
下面看scullpipe裝置對poll操作的實現,其內容其實非常簡單:
[cpp] view plaincopyprint?- 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}
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函式很簡單,那麼核心是怎麼實現poll和select系統呼叫的呢?當用戶空間程式呼叫poll和select函式時,核心會呼叫由使用者程式指定的全部檔案的poll方法,並向它們傳遞同一個poll_table結構。poll_table結構其實是一個生成”實際資料結構”的函式(名為poll_queue_proc)的封裝,這個函式poll_queue_proc,不同的應用場景,核心有不同的實現,這裡我們不仔細研究對應的函式。對於poll和select系統呼叫來說,這個”實際資料結構”是一個包含poll_table_entry結構的記憶體頁連結串列。這裡提到的相關資料結構在linux-2.6.32-38原始碼中,定義在include/linux/poll.h檔案中,程式碼如下所示:
[cpp] view plaincopyprint?- 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 inlinevoid 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 inlinevoid 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};
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裝置是一個完整的程式模組。