1.简介
epoll是Linux下多路复用IO接口select/poll的增强版本,他能显著提高程序在大量并发连接中只有少量活跃的情况下的系统cpu利用率,因为他会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,还有就是获取事件的时候,无需遍历整个被监听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
epoll提供电平触发和边沿触发两种方式,这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
2.相应函数
本质是一颗红黑树。
int epoll_create(int size);
size:创建的红黑树的监听节点数量。(仅供内核参考)
返回值:指向新创建的红黑树的根节点的fd。
失败:- 1 errno
int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
epfd:epoll_create函数的返回值。epfd。
op:对该监听红黑树所做的操作。
添加,移动,删除:EPOLL_CTL_ADD EPOLL_CTL_MOD EPOLL_CTL_DEL
EPOLL_CTL_ADD 添加fd到红黑树
EPOLL_CTL_MOD 修改fd监听红黑树上的监听事件’
EPOLL_CTL_DEL 将一个fd从监听红黑树上摘下(取消监听)
fd:待监听的fd
event: 本质是结构体的地址
events:
EPOLLIN/EPOLLOUT/EPOLLERR
data:联合体:
fd:对应前面的那个fd,对应监听事件的fd
struct epoll_event{
uint32_t events;
epoll_data_t data;
};
typedef union epoll_data{
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
}epoll_data_t;
int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout)
epfd:epoll_create函数的返回值。
events:【数组】,传出参数,满足监听条件的那些fd结构体。
只要在这个数组里面的都是满足条件的
maxevents:数组元素的总个数
timeout:超时时间
-1:阻塞
0:不阻塞
“>0” :超时时间
返回值:
">0"满足监听的总个数。可以用作循环上限。
代码实现:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/epoll.h>
#define SERV_PORT 8000
#define MAXLINE 8192
#define OPEN_MAX 5000
int main(int argc, char *argv[])
{
int i, listenfd, connfd, sockfd;
int n,num=0;
ssize_t nready,efd,res;
char buf[MAXLINE],str[INET_ADDRSTRLEN];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
struct epoll_event tep,ep[OPEN_MAX];
listenfd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
serv.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd, 20);
efd=epoll_create(OPEN_MAX);
if(efd==-1)
perr_exit("epoll_create error");
tep.events=EPOLLIN;
tep.data.fd=listenfd;
res=epoll_ctl(efd,EPOLL_CTL_ADD,listenfd,&tep);
if(res==-1)
perr_exit("epoll_ctl error");
for (;;)
{
nready = epoll_wait(efd,ep,OPEN_MAX,-1);
if(nready==-1)
perr_exit("epoll_wait error");
for(i=0;i<nready;i++){
if(!(ep[i].events&EPOLLIN))//如果不是读事件,继续循环
continue;
if(ep[i].data.fd==lestenfd){//判断是不是listenfd如果是的话说明是建立连接的读事件
clilen=sizeof(cliaddr);
connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&clilen);
tep.events=EPOLLIN;
tep.data.fd=connfd;
res=epoll_ctl(efd,EPOLL_CTL_ADD,connfd,&tep);
if(res==-1)
perr_exit("epoll_ctl error");
}
else
{
sockfd=ep[i].data.fd;
n=read(sockfd,buf,MAXLINE);
if(n==0)
{
res=epoll_ctl(efd,EPOLL_CTL_DEL,sockfd,NULL);//读到0说明客户端关闭连接,将该文件描述符从红黑树删除
if(res==-1)
perr_exit("epoll_ctl error");
close(sockfd);
}
else if(n<0)//出错,我们摘除节点关闭连接
{
res=epoll_ctl(efd,EPOLL_CTL_DEL,sockfd,NULL);
if(res==-1)
perr_exit("epoll_ctlb error");
close(sockfd);
}
else
{
for(i=0;i<n;i++)
buf[i]=toupper(buf[i]);
write(STDOUT_FILENO,buf,n);
write(sockfd,buf,n);
}
}
}
}
close(listenfd);
return 0;
}