[轉]Linux 2.6 核心Epoll用法舉例說明

epoll用到的所有函式都是在標頭檔案sys/epoll.h中宣告的,下面簡要說明所用到的資料結構和函式:

所用到的資料結構:

typedef union epoll_data {
                void *ptr;
                int fd;
                __uint32_t u32;
                __uint64_t u64;
        } epoll_data_t;

        struct epoll_event {
                __uint32_t events;      /* Epoll events */
                epoll_data_t data;      /* User data variable */
        };

結構體epoll_event 被用於註冊所感興趣的事件和回傳所發生待處理的事件,其中epoll_data 聯合體用來儲存觸發事件的某個檔案描述符相關的資料,例如一個client連線到伺服器,伺服器通過呼叫accept函式可以得到於這個client對應的socket檔案描述符,可以把這檔案描述符賦給epoll_data的fd欄位以便後面的讀寫操作在這個檔案描述符上進行。epoll_event 結構體的events欄位是表示感興趣的事件和被觸發的事件,常用的取值為:

EPOLLIN :表示對應的檔案描述符可以讀;

EPOLLOUT:表示對應的檔案描述符可以寫;

EPOLLPRI:表示對應的檔案描述符有緊急的資料可讀;

EPOLLERR:表示對應的檔案描述符發生錯誤;

EPOLLHUP:表示對應的檔案描述符被結束通話;

EPOLLET:設定為 Edge Triggered 模式。

所用到的函式:

1、epoll_create函式

函式宣告:int epoll_create(int size)

2、epoll_ctl函式

函式宣告:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

該函式用於控制某個檔案描述符上的事件,可以註冊事件,修改事件,刪除事件。

引數:epfd:由 epoll_create 生成的epoll專用的檔案描述符;

op:要進行的操作例如註冊事件,可能的取值EPOLL_CTL_ADD 註冊、EPOLL_CTL_MOD 修改、EPOLL_CTL_DEL 刪除

fd:關聯的檔案描述符;

event:指向epoll_event的指標;

如果呼叫成功返回0,不成功返回-1

3、epoll_wait函式

函式宣告:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)

該函式用於輪詢I/O事件的發生;

引數:

epfd:由epoll_create 生成的epoll專用的檔案描述符;

epoll_event:用於回傳待處理事件的陣列;

maxevents:每次能處理的事件數;

timeout:等待I/O事件發生的超時值;

返回發生事件數。

例子:

#include <iostream>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

#define MAXLINE 10
#define OPEN_MAX 100
#define LISTENQ 20
#define SERV_PORT 5555
#define INFTIM 1000

void setnonblocking(int sock)
{
     int opts;
     opts=fcntl(sock,F_GETFL);

     if(opts < 0)
     {
          perror("fcntl(sock,GETFL)");
          exit(1);
     }
     opts = opts|O_NONBLOCK;

     if(fcntl(sock,F_SETFL,opts) < 0)
     {
          perror("fcntl(sock,SETFL,opts)");
          exit(1);
     } 
}

int main()
{
     int i, maxi, listenfd, connfd, sockfd,epfd,nfds;
     ssize_t n;
     char line[MAXLINE];
     socklen_t clilen;
     //宣告epoll_event結構體的變數,ev用於註冊事件,陣列用於回傳要處理的事件
     struct epoll_event ev,events[20];
     //生成用於處理accept的epoll專用的檔案描述符
     epfd=epoll_create(256);

     struct sockaddr_in clientaddr;
     struct sockaddr_in serveraddr;
     listenfd = socket(AF_INET, SOCK_STREAM, 0);
     //把socket設定為非阻塞方式
      setnonblocking(listenfd);
     //設定與要處理的事件相關的檔案描述符
      ev.data.fd=listenfd;
     //設定要處理的事件型別
      ev.events=EPOLLIN|EPOLLET;
     //註冊epoll事件
      epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

     bzero(&serveraddr, sizeof(serveraddr));
     serveraddr.sin_family = AF_INET;

     char *local_addr="200.200.200.204";
     inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT);
     serveraddr.sin_port=htons(SERV_PORT);
     bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));
     listen(listenfd, LISTENQ);

     maxi = 0; 
     for ( ; ; ) {
         //等待epoll事件的發生
          nfds=epoll_wait(epfd,events,20,500);
         //處理所發生的所有事件      
          for(i=0;i < nfds;++i)
         {
             if(events[i].data.fd==listenfd)
             {
                  connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);
                  if(connfd < 0){
                       perror("connfd < 0");
                       exit(1);
                  }
                  setnonblocking(connfd);

                  char *str = inet_ntoa(clientaddr.sin_addr);
                  std::cout<<"connect from "<<str<<std::endl;
                  //設定用於讀操作的檔案描述符
                     ev.data.fd=connfd;
                  //設定用於注測的讀操作事件
                     ev.events=EPOLLIN|EPOLLET;
                  //註冊ev
                  epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
             }
             else if(events[i].events&EPOLLIN)
             {
                 if ( (sockfd = events[i].data.fd) < 0) continue;
                 if ( (n = read(sockfd, line, MAXLINE)) < 0) {
                      if (errno == ECONNRESET) {
                           close(sockfd);
                           events[i].data.fd = -1;
                      } else 
                           std::cout<<"readline error"<<std::endl;
                 } else if (n == 0) {
                      close(sockfd);
                      events[i].data.fd = -1;
                 }
                 //設定用於寫操作的檔案描述符
                    ev.data.fd=sockfd;
                 //設定用於注測的寫操作事件
                    ev.events=EPOLLOUT|EPOLLET;
                 //修改sockfd上要處理的事件為EPOLLOUT
                 epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
             }
             else if(events[i].events&EPOLLOUT)
             {    
                 sockfd = events[i].data.fd;
                 write(sockfd, line, n);
                 //設定用於讀操作的檔案描述符
                    ev.data.fd=sockfd;
                 //設定用於注測的讀操作事件
                    ev.events=EPOLLIN|EPOLLET;
                 //修改sockfd上要處理的事件為EPOLIN
                 epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
             }

         }
     }
}