2016年11月9日 星期三

epoll 介紹

最近把 C10K 問題重新溫習一遍,針對裡面提到的幾個技術,有些時間好好看一下。第一個注意到的就是 linux 系統下的 epoll。

epoll 與 select 的差異

  • select() 有個問題,其最大個數限制 FD_SETSIZE,預設值可能是 1024 或 2048
  • epoll 上限是系統最大可以打開文件的數目,執行 "cat /proc/sys/fs/file-max" 可檢視此上限值。在 Linux 2.6.28之後,要檢視 /proc/sys/fs/epoll/max_user_watches 
  • 另外 epoll 透用 mmap 讓 kernel space 與 user space 共享同一塊內存,減少內存拷貝。而 select() 則需要較多的內存拷貝。

epoll 支援兩種工作模式
LT(level triggered)
支援 block 和 no-block socket,在事件狀態未變更前將不斷被觸發。(直到緩衝區資料全部被讀取)
ET(edge-triggered)
僅支援 no-block socket,在事件狀態改變時只觸發一次。

三個API
int epoll_create(int maxfds);
建立 epoll 文件描述符 
int epoll_ctl (int epfd, int op, int fd, struct epoll_event *event);
增刪或修改文件描述符,這些描述符的集合又稱為 epoll set。
op 可以是 EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD 
epoll_event 結構如下:
struct epoll_event
{
  uint32_t events; /* Epoll events */
  epoll_data_t data; /* User data variable */
}; 
新增 event 時要設定事件以及文件描述符,如下: 
setnonblocking(fd);
ev.events=EPOLLIN|EPOLLET;
ev.data.fd=fd; 
// EPOLLET:工作模式設定為 edge-triggered,若不設定則是 level triggered
// EPOLLONESHOT: 一個事件發生並且讀取之後,不再監聽此文件描述符
// EPOLLIN是監聽讀的事件,若需要監聽寫的事件,可加入EPOLLOUT
int epoll_wait(int epfd, epoll_event *events, int max events, int timeout)
等待用戶感興趣的 IO事件發生, 若有事件發生會返回事件個數,並將事件放入 events。若要關閉 epoll 文件描述符, 直接使用 close() 即可。

程式範例
epollfd = epoll_create1(0);

event.events = EPOLLIN;
event.data.fd = serverfd;
epoll_ctl(epollfd, EPOLL_CTL_ADD, serverfd, &event);

while(true) {
 nfds  = epoll_wait(epollfd, &events, MAXEVENTS, timeout);
 for(i = 0; i < nfds; ++i) {
  if(events[i].data.fd == serverfd) {
   conn_sock = accpet(listen_sock,(struct sockaddr *) &addr, &addrlen);
   setnonblocking(conn_sock);                       
   
   ev.events = EPOLLIN | EPOLLET;
            ev.data.fd = conn_sock;
            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
    perror("epoll_ctl: conn_sock");
    exit(EXIT_FAILURE);
   }
   
  } else {
   do_use_fd(events[n].data.fd);
  }
 }
}


參考資料