epoll、poll和select優缺點
select
優點
(1)select()的可移植性更好,在某些Unix系統上不支持poll()
(2)select() 對於超時值提供了更好的精度:微秒,而poll是毫秒
缺點
(1)每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大
(2)同時每次調用select都需要在內核遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大
(3)select支持的文件描述符數量太小了,默認是1024
poll
優點
(1)poll() 不要求開發者計算最大文件描述符加一的大小。
(2)poll() 在應付大數目的文件描述符的時候相比於select速度更快
(3)它沒有最大連接數的限制,原因是它是基於鏈表來存儲的。
缺點
(1)大量的fd的數組被整體複製於用戶態和內核地址空間之間,而不管這樣的複製是不是有意義。
(2)與select一樣,poll返回後,需要輪詢pollfd來獲取就緒的描述符
epoll
epoll底層實現
在調用epoll_create時操作系統會創建一顆紅黑樹存放socket和一個隊列存放就緒事件
優點
(1)支持一個進程打開大數目的socket描述符(FD)
(2)IO效率不隨FD數目增加而線性下降
(3)使用mmap加速內核與用戶空間的消息傳遞。
epoll服務器實現
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#define MAX_EPOLL_EVENTS 64
static void usage(char* arg)
{
printf("Usage:%s [local_ip][local_port]\n",arg);
}
int starup(const char* ip,int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
perror("socket");
exit(2);
}
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip);
int opt = 1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
if(bind(sock,(struct sockaddr*)&server,sizeof(server)) < 0)
{
perror("bind");
exit(3);
}
if(listen(sock,5) < 0)
{
perror("listen");
exit(4);
}
return sock;
}
int main(int argc,char* argv[])
{
if(3 != argc)
{
usage(argv[0]);
return 1;
}
int listen_sock = starup(argv[1],atoi(argv[2]));
int epfd = epoll_create(200);
if(epfd < 0)
{
perror("epoll_create");
return 5;
}
struct epoll_event ev,ev_array[MAX_EPOLL_EVENTS];
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if(epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&ev) != 0)
{
perror("epoll_ctl");
return 6;
}
while(1)
{
int num = epoll_wait(epfd,ev_array,MAX_EPOLL_EVENTS,10000);
switch(num)
{
case -1:
{
perror("epoll_wait");
return 7;
break;
}
case 0:
{
printf("nothing ready...\n");
break;
}
default:
{
int i;
for(i = 0;i < num; ++i)
{
if(ev_array[i].data.fd == listen_sock && ev_array[i].events & EPOLLIN)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len);
if(new_sock < 0)
{
perror("accept");
continue;
}
printf("get a new client:%s,%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
struct epoll_event event;
event.events = EPOLLIN | EPOLLOUT;
event.data.fd = new_sock;
if(epoll_ctl(epfd,EPOLL_CTL_ADD,new_sock,&event) != 0)
{
perror("epoll_ctl");
close(new_sock);
continue;
}
}
else if(ev_array[i].events & EPOLLIN)
{
char buf[1024];
ssize_t s = read(ev_array[i].data.fd,buf,sizeof(buf)-1);
if(s<0)
{
perror("read:EPOLLIN");
if(epoll_ctl(epfd,EPOLL_CTL_DEL,ev_array[i].data.fd,&ev_array[i])!=0)
{
perror("epoll_ctl");
continue;
}
close(ev_array[i].data.fd);
continue;
}
else if(s == 0)
{
printf("client quit...\n");
epoll_ctl(epfd,EPOLL_CTL_DEL,ev_array[i].data.fd,&ev_array[i]);
close(ev_array[i].data.fd);
continue;
}
else
{
buf[s]=0;
printf("%s\n",buf);
}
}
else if(ev_array[i].events & EPOLLOUT)
{
char* buf = "hello word";
write(ev_array[i].data.fd,buf,strlen(buf));
epoll_ctl(epfd,EPOLL_CTL_DEL,ev_array[i].data.fd,&ev_array[i]);
close(ev_array[i].data.fd);
printf("client is quited\n");
continue;
}
}//for
}
}//switch
}
return 0;
}