Linux基於事件驅動的程式框架(轉載)
[轉]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); } } } } |