【Linux】多路復用之—epoll


一、多路復用之——epoll

int epoll_create(int size);

int epoll_ctl(int epfd,int op,int fd,struct epoll_event* event);

int epoll_wait(int epfd,struct epoll_event* events,int maxevents,int timeout);

1、epoll_create:創建一個epoll句柄,size參數可以忽略

當創建一個句柄后,會占用一個fd值,故在使用完后,要close(fd)。

2、epoll_ctl:注冊要監聽的事件類型

參數:(1)epfd:epoll_create的返回值;

   (2)op:EPOLL_CTL_ADD注冊新fd到epfd中

    EPOLL_CTL_MOD修改已經注冊的fd

    EPOLL_CTL_DEL               從epfd中刪除一個fd

   (3)fd:要監聽的fd;

   (4)event:監聽的事件;

struct epoll_event{

_uint32_t    events;

epoll_data_t    data;

}

typedef union epoll_data{

void*    ptr;

int fd;

_uint32_t U32;

_uint64_t U64;

}

   (5)events集合:

EPOLLIN:表示對應的文件描述符可以讀;

EPOLLOUT:表示對應的文件描述符可以寫;

EPOLLPRI:表示對應的文件描述符有緊急事件可讀;

EPOLLERR:表示對應的文件描述符發生錯誤;

EPOLLHUP:表示對應的文件描述符被掛斷;

EPOLLET:將EPOLL設為邊緣觸發模式;

EPOLLLT:將EPOLL設為水平觸發模式;

EPOLLONESHOT:只監聽一次事件;

3、epoll_wait:收集在epoll監控的事件中已經發生的事件

參數:(1)epfd:創建的epoll句柄;

   (2)events:已分配好的epoll_events結構體,epoll會將發生的事件放到events中;

   (3)maxevents:events的大小;

   (4)timeout:NULL:沒有timeout,一直阻塞等待知道某個事件發生;

    0:僅檢測描述符集合的狀態,然后立即返回;

    特定值:等待timeout時間,如果沒有發生,超時返回。

4、epoll的兩種工作模式:

epoll工作在ET模式的時候,必須使用非阻塞套接口,以避免由於一個文件句柄的阻塞讀、阻塞寫操作把處理多個文件描述符的任務餓死。

epoll工作在LT模式的時候,在收到多個數據的時候仍然會產生多個事件,支持阻塞和非阻塞接口,這樣,內核告訴你一個文件描述符是否就緒了,然后你可以進行I/O,若你不做任何操作,內核還是會繼續通知你的,錯誤率小。

ET與LT的區別在於,當一個新事件到來時,ET模式下當然可以從epoll_wait調用中獲取到這個事件,可是如果這次沒有把這個事件對應的套接字緩沖區處理完,在這個套接字中沒有新事件到來時,ET模式下無法再次獲取數據。但LT正好相反,只要一個事件對應的套接字緩沖區還有數據,就能夠獲取。

5、epoll模型的優點:

(1)使用內存映射(mmap)技術,避免用戶到內存的拷貝;

(2)epoll事先通過epoll_ctl()來注冊一個文件描述符,一旦基於某個文件描述符就緒時,內核會采用類似callback的回調機制,迅速激活這個文件描述符,當進程調用epoll_wait時使得到通知;

(3)監視的文件描述符數量不受限制,它所支持的fd上限是最大可以打開文件的數目;

(4)I/O的效率不會隨着fd數量的增加而下降,select、poll實現需要自己不斷輪詢所有fd集合,指到設備就緒,期間可能要睡眠和喚醒多次交替。而epoll其實也需要調用epoll_wait不斷輪詢就緒鏈表,期間也可能睡眠和喚醒多次交替,但是它是設備就緒時,調用回調函數,把就緒fd放入就緒鏈表中,並喚醒在epoll_wait中進入睡眠的進程。雖然都要睡眠和交替,但select和poll在“醒着”的時候要遍歷整個fd集合,而epoll在“醒着”的時候只需要遍歷就緒鏈表是否為空就行了。這節省了大量的CPU時間。這就是回調機制的性能提升。


代碼示例:

epoll_server.c

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<fcntl.h>
#define MAX_NUM 64
int startup(int port)
{
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0){
perror("socket");
exit(1);
}
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(port);
local.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0){
perror("bind");
exit(2);
}
if(listen(sock,5)<0){
perror("listen");
exit(3);
}
return sock;
}
//set file descriptor for no blocking mode
void set_nonoblock(int _fd)
{
int fl=fcntl(_fd,F_GETFL);
if(fl<0){
perror("fcntl");
return;
}
fcntl(_fd,fl | O_NONBLOCK);
return;
}
//recv data
int read_data(int _fd,char* buf,int len)
{
ssize_t _size=-1;
int total=0;
while((_size=recv(_fd,buf+total,len-1,MSG_DONTWAIT))){
if(_size>0){
total+=_size;
}else if(_size==0){//file end
return 0;
}else{
return -1;
}
}
}
int main()
{
short port=8080;
int listen_sock=startup(port);
struct sockaddr_in client;
socklen_t len=sizeof(client);
int epoll_fd=epoll_create(256);//create epoll handle
int timeout=1000;
if(epoll_fd<0){
perror("epoll_create");
exit(1);
}
struct epoll_event _ev;//save care fd
_ev.events=EPOLLIN;
_ev.data.fd=listen_sock;
if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,listen_sock,&_ev)<0){
perror("epoll_ctl");
goto LABLE;
}
struct epoll_event _ev_out[MAX_NUM];//save ready fd
char buf[1024*5];
memset(buf,'\0',sizeof(buf));
int ready_num=-1;//save ready_fd num
while(1){
switch(ready_num=epoll_wait(epoll_fd,_ev_out,MAX_NUM,timeout)){
case 0:
printf("timeout\n");
break;
case -1:
perror("epoll_wait");
break;
default:
{
int i=0;
for(;i<ready_num;i++){
int _fd=_ev_out[i].data.fd;
//listen_sock
if(_fd==listen_sock && (_ev_out[i].events & EPOLLIN)){
int new_sock=accept(_fd,(struct sockaddr*)&client,&len);
if(new_sock<0){
perror("accept");
continue;
}
printf("get a new connect...\n");
set_nonoblock(new_sock);
_ev.events=EPOLLIN | EPOLLET;
_ev.data.fd=new_sock;
if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,new_sock,&_ev)<0){
perror("epoll_ctl");
close(new_sock);
continue;
}
continue;
}
//data_sock
if(_ev_out[i].events & EPOLLIN){
if(read_data(_fd,buf,sizeof(buf))==0){
printf("client close...");
epoll_ctl(epoll_fd,EPOLL_CTL_DEL,_fd,NULL);
}
printf("%s\n",buf);
}
}
}
break;
}
}
LABLE:
close(epoll_fd);
return 0;
}

Makefile:

epoll_server:epoll_server.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -rf epoll_server



注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com