一 内核事件表
epoll不同于select和poll,它是使用一组函数来完成任务;同时,epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中,从而不像select 和 poll每次调用都需要重复传入文件描述符集或事件集。但是epoll需要一个额外的文件描述符来表示内核中的这个事件表。
//创建事件表函数
#include<sys/epoll.h>
int epoll_create( int size )
参数介绍:size 现在并不起作用,只是给内核一个提示,告诉它这个表需要多大。
返回值:该函数的返回值,指定要访问的内核事件表。一般用于其他epoll系统调用的第一个参数。
//操作内核事件表的函数
int epoll_ctl( int epfd , int op , int fd, struct epoll_event *event)
参数介绍:fd是要操作的文件描述符;op是指定操作类型,通常为下面这三种:
EPOLL_CTL_ADD | 往注册表中注册fd上的事件 |
EPOLL_CTL_MOD | 修改fd上的注册事件 |
EPOLL_CTL_DEL | 删除fd上的注册事件 |
event参数指定事件,它是epoll_event结构体指针。定义如下
struct epoll_event
{
_uint32_t events; // epoll事件
epoll_data_t data; //用户数据
};
其中events,是成员描述事件类型(epoll支持poll的大部分,同时还多了EPOLLET和EPOLLONESHOT);data成员用于储存用户数据,其类型定义如下:
typedef union epoll_data
{
void* ptr;
int fd;
uint32_t u32;
uint64_t u64;
}epoll_data_t;
epoll_data_t是一个联合体,使用最多的是fa,它指定了事件所从属的目标文件描述符。
epoll_ctl成功返回0,失败返回-1并设置errno。
//epoll系统调用的主要接口
int epoll_wait(int epfd , struct epoll_event* events,int maxevents , int timeout);
返回值:成功返回就绪的文件描述符的个数,失败发挥-1,并设置errno。
参数:timeout参数的含义和poll函数的timeout参数相同;maxevents参数指定最多监听多少个事件,它必须大于0。
函数功能:如果检测到事件,就将所有就绪的事件从内核事件表中(由epfd指定)中复制到它的第二个参数的events指向的数组中:这个数组只用来输出epoll_wait检测到的就绪事件。不想前两者即用于传入all文件描述符,有用于输出内核检测到的就绪的文件描述符,这样就极大提高了应用程序索引就绪文件描述符的效率。
代码测试
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<assert.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/epoll.h>
#define SIZE 20
int main()
{
//完成TCP的连接
int sockfd = socket(AF_INET,SOCK_STREAM,0);
assert( sockfd != -1);
struct sockaddr_in ser,cli;
memset(&ser,0,sizeof(ser));
ser.sin_family = AF_INET;
ser.sin_port = htons(6000);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
assert( res != -1);
listen(sockfd,5);
int epollfd = epoll_create(5);
assert(epollfd != -1);
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = sockfd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);
while(1)
{
struct epoll_event events[SIZE];
int n = epoll_wait(epollfd,events,SIZE,-1);
if(n<=0)
{
printf("epoll wait fail:\n");
continue;
}
int i = 0;
for(;i < n;++i)
{
int fd = events[i].data.fd;
if(fd == sockfd)
{
int len = sizeof(cli);
int c = accept(fd,(struct sockaddr*)&cli,&len);
if(len < 0)
{
printf("accept is fail:\n");
continue;
}
event.events = EPOLLIN | EPOLLRDHUP;
event.data.fd = c;
epoll_ctl(epollfd,EPOLL_CTL_ADD,c,&event);
}
else if(events[i].events & EPOLLRDHUP)
{
epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,NULL);
close(fd);
printf("%d was over\n",fd);
}
else if(events[i].events & EPOLLIN)
{
char buff[128] = {0};
recv(fd,buff,127,0);
printf("%d: %s\n",fd,buff);
}
}
}
close(sockfd);
}