- epoll介绍
- 相关函数
- 相关结构体
- 例子
一、epoll
Epoll 是一种高效的管理socket的模型,相对于select和poll来说具有更高的效率和易用性。传统的select以及poll的效率会因为 socket数量的线形递增而导致呈二次乃至三次方的下降,而epoll的性能不会随socket数量增加而下降。
二、相关函数
头文件:#include <sys/epoll.h>
-
int epoll_create(int size):
功能:生成一个epoll专用的文件描述符epollfd.
参数size:用来告诉内核要监听的数目一共有多少个。
返回值:成功时,返回一个非负整数的文件描述符,作为创建好的epoll句柄。调用失败时,返回-1,错误信息可以通过errno获得。
说明:创建一个epoll句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。 -
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。
参数epfd:epoll_create()函数返回的epoll句柄。
参数op:要进行的操作EPOLL_CTL_ADD 注册。
其中参数op的可选值有以下3个:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
参数fd:进行操作的目标文件描述符listen_sock。
参数event:struct epoll_event结构指针,将fd和要进行的操作关联起来。
返回值:成功时,返回0,作为创建好的epoll句柄。调用失败时,返回-1,错误信息可以通过errno获得。
说明:控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。这个系统调用用于操作epoll函数所生成的实例(该实例由epfd指向),向fd实施op操作。 -
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
功能:等待事件epoll_event(EPOLLIN)的产生。并将发生的sokct fd和事件类型放入到events数组中。
参数epfd:epoll_create()函数返回的epoll句柄。
参数events:struct epoll_event结构指针,用于回传代处理事件的数组;用来从内核得到事件的集合。
参数 maxevents:告诉内核这个events有多大
参数 timeout: 等待时的超时时间,以毫秒为单位。
返回值:成功时,返回需要处理的事件数目。调用失败时,返回0,表示等待超时。
说明:该函数用于轮询I/O事件的发生。
@epfd:由epoll_create 生成的epoll专用的文件描述符;
@epoll_event:用于回传代处理事件的数组;
@maxevents:每次能处理的事件数;
@timeout:等待I/O事件发生的超时值;
成功:返回发生的事件数;失败:-1
三、相关结构体 epoll_event
头文件 #include <fcntl.h>
struct epoll_event {
__uint32_t events; /* epoll event */
epoll_data_t data; /* User data variable */
}; //结构体epoll_event被用于注册所感兴趣的事件和回传所发生待处理的事件
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;//保存触发事件的某个文件描述符listen_sock相关的数据
其中events表示感兴趣的事件和被触发的事件,可能的取值为:
EPOLLIN :表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI: 表示对应的文件描述符有紧急的数可读;
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: ET的epoll工作模式;
四、例子
/*
*@brief:epoll接收
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include "x_log.h"
#define MAX_EVENTS 100
#define LISTENQ 10
#define DPORT 10003
//设置socket连接为非阻塞模式
void setnonblocking(int sockfd)
{
int opts = fcntl(sockfd,F_GETFL);
if(opts < 0)
{
perror("fcntl(sockfd,F_GETFL)\n");
exit(1);
}
opts = (opts | O_NONBLOCK);
if(fcntl(sockfd,F_SETFL,opts) < 0)
{
perror("fcntl(sockfd,F_SETFL)\n");
exit(1);
}
}
int main()
{
int listen_sock,conn_sock;
struct sockaddr_in clientaddr, serveraddr;
socklen_t socklen;
int epollfd;
int i,nfds;
struct epoll_event ev,events[100];//ev用于注册事件,数组用于回传要处理的事件
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if(0 > listen_sock)
{
perror("socket");
return -1;
}
//printf("listen_sock: %d\n",listen_sock);
setnonblocking(listen_sock);
//创建一个epoll对象
epollfd=epoll_create(MAX_EVENTS);//生成用于处理accept的epoll专用的文件描述符
ev.events = EPOLLIN|EPOLLET;//文件描述符可以读|ET边缘触发模式
ev.data.fd = listen_sock;
//注册epoll事件
epoll_ctl(epollfd,EPOLL_CTL_ADD,listen_sock,&ev);
//设置服务器端地址信息
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(DPORT);
serveraddr.sin_addr.s_addr = ntohl(INADDR_ANY);
//设置套接字选项避免地址使用错误
int on=1;
if((setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)
{
perror("setsockopt");
return -1;
}
//绑定
if(0 > bind(listen_sock,(struct sockaddr *)&serveraddr, sizeof(serveraddr)))
{
perror("bind");
return -1;
}
//监听
if(0 > listen(listen_sock,LISTENQ))
{
perror("listen");
return -1;
}
while(1)
{
nfds = epoll_wait(epollfd,events,MAX_EVENTS,200);
for(i=0;i<nfds;++i)//开始处理所发生的事件
{
if((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN)))
{
close (events[i].data.fd);
continue;
}
/*有新连接到来*/
else if(events[i].data.fd == listen_sock)
{
conn_sock = accept(listen_sock,(struct sockaddr *)&clientaddr, &socklen);
setnonblocking(conn_sock);
ev.events = EPOLLIN|EPOLLET;//设置用于读操作的事件
ev.data.fd = conn_sock;
epoll_ctl(epollfd,EPOLL_CTL_ADD,conn_sock,&ev);//注册ev
continue;
}
/*有新数据到来*/
else
{
//读取数据
// ......
}
}
}
close(conn_sock);
close(listen_sock);
return 0;
}