天天看點

poll模型詳解

1.poll模型原理

poll模型是基于select最大檔案描述符限制提出的,跟select一樣,隻是将select使用的三個基于位的檔案描述符改為使用一個數組的形式,對于各種可能的事件進行了一個包裝

#include <sys/poll.h>
int poll (struct pollfd *fds, unsigned int nfds, int timeout);      

參數說明:

第一個參數fds為一個pollfd結構數組,用來儲存檔案描述符

第二個參數nfds為pollfd結構體數組+1

第三個參數timeout為poll等待時間

傳回值:

正常傳回值為輪詢檔案描述符結構有事件發送的個數,-1傳回失敗

和select()不一樣,poll()沒有使用低效的三個基于位的檔案描述符set,而是采用了一個單獨的結構體pollfd數組,由fds指針指向這個組。pollfd結構體定義如下:

#include <sys/poll.h>

struct pollfd {
int fd; /* file descriptor */
short events; /* requested events to watch */
short revents; /* returned events witnessed */
};      

每一個pollfd結構體指定了一個被監視的檔案描述符,可以傳遞多個結構體,訓示poll()監視多個檔案描述符。每個結構體的events域是監視該檔案描述符的事件掩碼,由使用者來設定這個域。revents域是檔案描述符的操作結果事件掩碼。核心在調用傳回時設定這個域。events域中請求的任何事件都可能在revents域中傳回。合法的事件如下:

POLLIN 有資料可讀。

POLLRDNORM 有普通資料可讀。

POLLRDBAND 有優先資料可讀。

POLLPRI 有緊迫資料可讀。

POLLOUT 寫資料不會導緻阻塞。

POLLWRNORM 寫普通資料不會導緻阻塞。

POLLWRBAND 寫優先資料不會導緻阻塞。

POLLMSG SIGPOLL消息可用。

此外,revents域中還可能傳回下列事件:

POLLER 指定的檔案描述符發生錯誤。

POLLHUP 指定的檔案描述符挂起事件。

POLLNVAL 指定的檔案描述符非法。

這些事件在events域中無意義,因為它們在合适的時候總是會從revents中傳回。使用poll()和select()不一樣,你不需要顯式地請求異常情況報告。

POLLIN | POLLPRI等價于select()的讀事件,POLLOUT | POLLWRBAND等價于select()的寫事件。POLLIN等價于POLLRDNORM | POLLRDBAND,而POLLOUT則等價于POLLWRNORM。

例如,要同時監視一個檔案描述符是否可讀和可寫,我們可以設定events為POLLIN | POLLOUT。在poll傳回時,我們可以檢查revents中的标志,對應于檔案描述符請求的events結構體。如果POLLIN事件被設定,則檔案描述符可以被讀取而不阻塞。如果POLLOUT被設定,則檔案描述符可以寫入而不導緻阻塞。這些标志并不是互斥的:它們可能被同時設定,表示這個檔案描述符的讀取和寫入操作都會正常傳回而不阻塞。

timeout參數指定等待的毫秒數,無論I/O是否準備好,poll都會傳回。timeout指定為負數值表示無限逾時;timeout為0訓示poll調用立即傳回并列出準備好I/O的檔案描述符,但并不等待其它的事件。這種情況下,poll()就像它的名字那樣,一旦選舉出來,立即傳回。

傳回值和錯誤代碼

成功時,poll()傳回結構體中revents域不為0的檔案描述符個數;如果在逾時前沒有任何事件發生,poll()傳回0;失敗時,poll()傳回-1,并設定errno為下列值之一:

EBADF 一個或多個結構體中指定的檔案描述符無效。

EFAULT fds指針指向的位址超出程序的位址空間。

EINTR 請求的事件之前産生一個信号,調用可以重新發起。

EINVAL nfds參數超出PLIMIT_NOFILE值。

ENOMEM 可用記憶體不足,無法完成請求。

2.poll工作流程

1.定義一個用戶端連接配接數組

struct pollfd clients[OPEN_MAX];

2.調用poll輪詢檔案描述符事件

nready = poll(clients, maxi + 1, -1);

3.使用執行個體

引用一個簡單的poll伺服器例子

#include  <unistd.h>  
    #include  <sys/types.h>       /* basic system data types */  
    #include  <sys/socket.h>      /* basic socket definitions */  
    #include  <netinet/in.h>      /* sockaddr_in{} and other Internet defns */  
    #include  <arpa/inet.h>       /* inet(3) functions */  
      
    #include <stdlib.h>  
    #include <errno.h>  
    #include <stdio.h>  
    #include <string.h>  
      
      
    #include <poll.h> /* poll function */  
    #include <limits.h>  
      
    #define MAXLINE 10240  
      
    #ifndef OPEN_MAX  
    #define OPEN_MAX 40960  
    #endif  
      
    void handle(struct pollfd* clients, int maxClient, int readyClient);  
      
    int  main(int argc, char **argv)  
    {  
        int servPort = 6888;  
        int listenq = 1024;  
        int listenfd, connfd;  
        struct pollfd clients[OPEN_MAX];  
        int  maxi;  
        socklen_t socklen = sizeof(struct sockaddr_in);  
        struct sockaddr_in cliaddr, servaddr;  
        char buf[MAXLINE];  
        int nready;  
      
        bzero(&servaddr, socklen);  
        servaddr.sin_family = AF_INET;  
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
        servaddr.sin_port = htons(servPort);  
      
        listenfd = socket(AF_INET, SOCK_STREAM, 0);  
        if (listenfd < 0) {  
            perror("socket error");  
        }  
      
        int opt = 1;  
        if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {  
            perror("setsockopt error");  
        }  
      
        if(bind(listenfd, (struct sockaddr *) &servaddr, socklen) == -1) {  
            perror("bind error");  
            exit(-1);  
        }  
        if (listen(listenfd, listenq) < 0) {  
            perror("listen error");      
        }  
      
        clients[0].fd = listenfd;  
        clients[0].events = POLLIN;  
        int i;  
        for (i = 1; i< OPEN_MAX; i++)   
            clients[i].fd = -1;   
        maxi = listenfd + 1;  
      
        printf("pollechoserver startup, listen on port:%d\n", servPort);  
        printf("max connection is %d\n", OPEN_MAX);  
      
        for ( ; ; )  {  
            nready = poll(clients, maxi + 1, -1);  
            //printf("nready is %d\n", nready);  
            if (nready == -1) {  
                perror("poll error");  
            }  
            if (clients[0].revents & POLLIN) {  
                connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &socklen);  
                sprintf(buf, "accept form %s:%d\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);  
                printf(buf, "");  
      
                for (i = 0; i < OPEN_MAX; i++) {  
                    if (clients[i].fd == -1) {  
                        clients[i].fd = connfd;  
                        clients[i].events = POLLIN;  
                        break;  
                    }  
                }  
      
                if (i == OPEN_MAX) {  
                    fprintf(stderr, "too many connection, more than %d\n", OPEN_MAX);  
                    close(connfd);  
                    continue;  
                }  
      
                if (i > maxi)  
                    maxi = i;  
      
                --nready;  
            }  
      
            handle(clients, maxi, nready);  
        }  
    }  
      
    void handle(struct pollfd* clients, int maxClient, int nready) {  
        int connfd;  
        int i, nread;  
        char buf[MAXLINE];  
      
        if (nready == 0)  
            return;  
      
        for (i = 1; i< maxClient; i++) {  
            connfd = clients[i].fd;  
            if (connfd == -1)   
                continue;  
            if (clients[i].revents & (POLLIN | POLLERR)) {  
                nread = read(connfd, buf, MAXLINE);//讀取用戶端socket流  
                if (nread < 0) {  
                    perror("read error");  
                    close(connfd);  
                    clients[i].fd = -1;  
                    continue;  
                }  
                if (nread == 0) {  
                    printf("client close the connection");  
                    close(connfd);  
                    clients[i].fd = -1;  
                    continue;  
                }  
      
                write(connfd, buf, nread);//響應用戶端    
                if (--nready <= 0)//沒有連接配接需要處理,退出循環  
                    break;  
            }  
        }  
    }      

4.總結讨論

1.poll與select,epoll比較