目录
20.1 epoll模型简介
20.2 epoll API
20.3 epoll 服务器示例
20.1 epoll模型简介
epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
怎么说呢?emmm
epoll模型,就像服务端是一个boss,它有一个秘书,而那些请求执行命令的描述符则为boss的手下。
用select时,就是轮询手下看看哪个有交工了,这样耗时耗力,而且手下还不能多,多了顾不过来了。
于是,我们使用epoll模型,哪个手下要交工,跟秘书说一声,秘书再定时汇报给boss,这样boss就不用一直去轮询了,省时省力还能管更多的手下。
这就是epoll相较于select的优势。
查看一个进程打开最大数目的socket描述符数量:
cat /proc/sys/fs/file-max
设置最大打开文件描述符限制
sudo vi /etc/security/limits.conf
//写入以下配置(soft 软限制,hard 硬限制)
* soft nofile 66666
* hard nofile 100000
20.2 epoll API
#include<sys/epoll.h>
(1)int epoll_create( int size);
创建一个epoll句柄,参数size用于告诉内核监听的文件描述符个数,跟内存大小有关
返回epoll 文件描述符
(2)int epoll_ctl( int epfd, int op, int fd, struct epoll_event *event ); // 成功,返0,失败返-1
控制某个epoll监控的文件描述符上的事件:注册,修改,删除
参数释义:
epfd:为epoll的句柄
op:表示动作,用3个宏来表示
··· EPOLL_CTL_ADD(注册新的 fd 到epfd)
··· EPOLL_CTL_DEL(从 epfd 中删除一个 fd)
··· EPOLL_CTL_MOD(修改已经注册的 fd 监听事件)
event:告诉内核需要监听的事件
typedef union epoll_data
{
void* ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
}
epoll_data_t; /* 保存触发事件的某个文件描述符相关的数据 */
struct epoll_event
{
__uint32_t events; /* epoll event */
epoll_data_t data; /* User data variable */
};
/* epoll_event.events:
EPOLLIN 表示对应的文件描述符可以读
EPOLLOUT 表示对应的文件描述符可以写
EPOLLPRI 表示对应的文件描述符有紧急的数据可读
EPOLLERR 表示对应的文件描述符发生错误
EPOLLHUP 表示对应的文件描述符被挂断
EPOLLET 表示对应的文件描述符有事件发生
*/
//具体咋用看后面代码示例
(3)int epoll_wait( int epfd, struct epoll_event *events, int maxevents, int timeout);
等待所监控文件描述符上有事件的产生
参数释义:
events:用来从雷和得到事件的集合
maxevent:用于告诉内核这个event有多大,这个maxevent不能大于创建句柄时的size
timeout:超时时间
··· -1:阻塞
··· 0:立即返回
···>0:指定微秒
成功返回有多少个文件描述符准备就绪,时间到返回0,出错返回-1.
20.3 epoll模型示例
/*server.c*/
#include<stdio.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<errno.h>
#define MAXLINE 80
#define SERV_PORT 8000
#include OPEN_MAX 1024
int main(void)
{
struct sockaddr_in servaddr,cliaddr;
socklen_t cliaddr_len;
int i,j,maxi,listenfd,connfd,sockfd;
int nready,efd,res,client[OPEN_MAX];
ssize_t n;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
struct epoll_event tep,ep[OPEN_MAX];
listenfd = socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htonl(SERV_PORT);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
listen(listenfd,20);
maxfd = listenfd; //初始化
maxi = -1; //client[]的下标
for(i = 0 ; i < OPEN_MAX ; i++ )
client[i] = -1; //用-1初始化client
//套路开始
efd = epoll_create(OPEN_MAX); //创建句柄
if(efd == -1)
perrno("epoll_create");
tep.events = EPOLLIN; //设置读事件
tep.data.fd = listenfd; //套接socket文件描述符
res = epoll_ctl(efd,EPOLL_CTL_ADD,listened,&tep); //将listenfd加入监听文件表,监听listenfd的读取内容
if(res == -1)
perrno("epoll_ctl");
while(1)
{
nready = epoll_wait(efd,ep,OPEN_MAX,-1); //阻塞监听
if(nready == -1)
perrno("epoll_wait error:");
for(i == 0; i<nready; i++)
{
if(!ep[i].event & EPOLLIN)
continue;
if(ep[i].data.fd == listenfd)
{
//开始接收数据了
printf("Accepting connections··· \n"); //写完一定要来检查一下这个换行,一不小心就忘记了
cliaddr_len = sizeof(cliaddr); //这得实时更新
connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len); //接收连接
printf("Read from %s at port %d \n",inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));
/*将客户端的地址读取到str里面然后打印*/ /*将端口号转换成整形数输出*/
for(j = 0;j < OPEN_MAX; j++)
{
if(client[j] < 0)
client[j] = connfd; //保存accept返回的文件描述符到client【】里
//break;
if( j == OPEN_MAX )
{
printf("Too many clients \n",stderr);
exit(-1);
}
if(j > maxi)
maxi = j;
tep.events = EPOLLIN;
tep.data.fd = connfd;
res = epoll_ctl(efd,EPOLL_CTL_ADD,connfd,&tep); //将connfd加入监听文件表,监听connfd的读取内容
if(res == -1)
perrno("epoll_ctl");
else
{
sockfd = ep[i].data.fd;
n = read(sockfd,buf,MAXLINE);
if(n == 0)
{
for(j = 0 ; j <= maxi ; j++)
{
if(client[i] == sockfd)
{
client[j] = -1;
break;
}
}
res = epoll_ctl(efd,EPOLL_CTL_ADD,sockfd,&tep); //将sockfd加入监听文件表,监听sockfd的读取内容
if(res == -1)
perrno("epoll_ctl")
close(sockfd);
printf("Client[%d] closed connetion \n",j);
}
else
{
for(j = 0; j<n; j++)
buf[j] = toupper(buf[j]);
write(sockfd,buf,n);
}
}
}
}
close(listenfd);
close(efd)
return 0;;
}