epoll介绍

1、epoll的优点(与select、poll对比)

i、支持一个进程打开大数目的socket描述符;

select()所支持打开的FD是有一定限制,FD_SETSIZE默认值是1024/2048。epoll没有描述符数量限制,一般是1G内存可以有10万左右,可通过使用cat /proc/sys/fs/file-max查看。

ii、IO效率不随FD数目增加面线性下降;

select/poll每次调用都会线性扫描全部的集合(同时还会轮询),导致效率呈现线性下降。而epoll不存在这个问题,因为epoll会为每个描述符FD注册一个回调函数,只有“活跃”的socket才会主动调用callback函数,其他idle状态socket则不会。当调用callback函数的FD,才返回相应的事件。

iii、使用mmap加速内核与用户空间的消息传递;

select、poll、epoll都需要内核把fd消息通知给用户空间。而epoll通过内核与用户空间mmap处于同一块内存实现消息的传递。

2、实现epoll的相关接口函数

epoll_create函数

#include<sys/epoll.h>
int epoll_create(int size);//告诉内核监听fd的数目
		//返回一个epoll句柄epfd,后续均要用着,在使用完epoll后,必须调用close关闭epfd,否则可能导致fd被耗尽

epoll_ctl事件注册函数

#include<sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);

struct epoll_event {
	_uint32_t event;//Epoll events
	epoll_data_t data;//User data variable
};
typedef union epoll_data {
	void* ptr;
	int fd;
	uint32_t u32;
	uint64_t u64;
}epoll_data_t;

此函数先要注册要监听的事件类型(如listenfd);

第一个参数是epoll_create()的返回值;

第二个参数表示动作,用3个宏来表示:

1、EPOLL_CTL_ADD,注册新的fd到epfd中;

2、EPOLL_CTL_MOD,修改已经注册的fd的监听事件;

3、EPOLL_CTL_DEL,从epfd中删除一个fd;

第三个参数是需要监听的fd

第四个参数 告诉内核需要监听什么事,其中struct epoll_event结构体中的events可以是以下几个宏的集合,EPOLLIN(表示对应文件描述符可以读(包括对端socket正常关闭)),EPOLLOUT(表示对应文件描述符可以写),EPOLLPRI(表示对应文件描述符有紧急的数据可读),EPOLLERR(表示对应文件描述符发生错误),EPOLLHUP(表示对应文件描述符被挂断),EPOLLET(将EPOLL设为边缘触发模式,这是相对水平触发而言),EPOLLONESHOT(只监听一次事件,当监听完这次事件后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里)。

epoll_wait等待事件发生函数

#include<sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);//等待事件的发生
				//返回需要处理的事件数目,返回0表示已超时

参数events(结构数组)用来从内核得到事件的集合,maxevents告诉内核这个events有多大,且maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时间(ms为单位,0会立即返回,-1将不确定或称永久阻塞)。

3、利用epoll编写一个回谢服务器

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/epoll.h>
#include<unistd.h>
#include<sys/types.h>

#define IPADDRESS "127.0.0.1"
#define PORT 9877
#define MAXSIZE 1024
#define LISTENQ 5
#define FDSIZE	1000
#define EPOLLEVENTS 100

#define SA struct sockaddr


void add_event(int epollfd,int fd,int state);
void handle_events(int epollfd,struct epoll_event* events,int num,int listenfd,char* buf);
void handle_accpet(int epollfd,int listenfd);
void do_read(int epollfd,int fd,char* buf);
void do_write(int epollfd,int fd,char* buf);
void delete_event(int epollfd,int fd,int state);
void modify_event(int epollfd,int fd,int state);

int main(){
	int listenfd,epollfd;
	struct sockaddr_in serv;
	struct epoll_event events[EPOLLEVENTS];
	char buf[MAXSIZE];
	int ret;
	

	listenfd=socket(AF_INET,SOCK_STREAM,0);
	
	bzero(&serv,sizeof(serv));
	serv.sin_family=AF_INET;
	serv.sin_port=htons(PORT);
	serv.sin_addr.s_addr=htonl(INADDR_ANY);
	
	bind(listenfd,(SA*)&serv,sizeof(serv));
	listen(listenfd,LISTENQ);

	bzero(buf,MAXSIZE);
	epollfd=epoll_create(FDSIZE);//create a fd. FDSIZE represent the maximum number of fd.
	add_event(epollfd,listenfd,EPOLLIN);//add fd(listenfd) to epollfd
	while(true){
		ret=epoll_wait(epollfd,events,EPOLLEVENTS,-1);//-1 representnever block.
		hand_events(epollfd,events,ret,listenfd,buf);
	}
	close(epollfd);
}
/*
struct epoll_event{
	_uint32_t events;//Epoll events
	epoll_data_t data;//User data variable
};

typedef union epoll_data{
	void* ptr;
	int fd;
	uint32_t u32;
	uint64_t u64;
}epoll_data_t;
*/


void add_event(int epollfd,int fd,int state){
	struct epoll_event ev;
	ev.events=state;
	ev.data.fd=fd;
	epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);//epoll事件注册函数,表明监听何种类型的事件
}

void hand_events(int epollfd,struct epoll_event *events,int num,int listenfd,char* buf){
	int fd;
	for(int i=0;i<num;++i){
		fd=events[i].data.fd;//process each active fd
		if((fd==listenfd)&&(events[i].events*EPOLLIN))//此处处理多个客户连接
			handle_accept(epollfd,listenfd);
		else if(events[i].events&EPOLLIN)//the active fd is readable
			do_read(epollfd,fd,buf);
		else if(events[i].events&EPOLLOUT)//the active fd is writable
			do_write(epollfd,fd,buf);
	}
}


void handle_accept(int epollfd,int listenfd){
	int child;
	struct sockaddr_in cliaddr;
	socklen_t clilen=sizeof(cliaddr);
	clifd=accept(listenfd,(SA*)&cliaddr,&clilen);
	printf("accept a new client: %s:%d",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
	add_event(epollfd,clifd,EPOLLIN);
}
void do_read(int epollfd,int fd,char* buf){
	int nread;
	nread=read(fd,buf,MAXSIZE);
	if(nread==-1){
		perror("read error:");
		close(fd);
		delete_event(epoll,fd,EPOLLIN);
	}else if(nread==0){
		fprintf(stderr,"client close.\n");
		close(fd);
		delete_event(epollfd,fd,EPOLLIN);
	}else{
		printf("read message is: %s",buf);
		modify_event(epollfd,fd,EPOLLOUT);
	}
}

void do_write(int epollfd,int fd,char* buf){
	int nwrite;
	if(nwrite==-1){
		perror("write error:");
		close(fd);
		delete_event(epollfd,fd,EPOLLOUT);
	}else{
		modify_event(epollfd,fd,EPOLLIN);
	}
	bzero(buf,MAXSIZE);
}

void delete_event(int epollfd,int fd,int state){
	struct epoll_event ev;
	ev.events=state;
	ev.data.fd=fd;
	epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
}

void modify_event(int epollfd,int fd,int state){
	struct epoll_event ev;
	ev.events=state;
	ev.data.fd=fd;
	epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
}

4、利用epoll编写一个客户端(大多数代码参照服务器)

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/epoll.h>
#include<unistd.h>
#include<sys/types.h>
#include<arpa/inet.h>

#define IPADDRESS "127.0.0.1"
#define SERV_PORT 9877
#define MAXSIZE 1024
#define LISTENQ 5
#define FDSIZE	1000
#define EPOLLEVENTS 100

#define SA struct sockaddr


void add_event(int epollfd,int fd,int state);
void handle_events(int epollfd,struct epoll_event* events,int num,int listenfd,char* buf);
void handle_accpet(int epollfd,int listenfd);
void do_read(int epollfd,int fd,char* buf);
void do_write(int epollfd,int fd,char* buf);
void delete_event(int epollfd,int fd,int state);
void modify_event(int epollfd,int fd,int state);

int count=0;
int main(int argc,char* argv[]){
	int sockfd,epollfd;
	struct sockaddr_in serv;
	struct epoll_event events[EPOLLEVENTS];
	char buf[MAXSIZE];
	int ret;
	

	sockfd=socket(AF_INET,SOCK_STREAM,0);
	
	bzero(&serv,sizeof(serv));
	serv.sin_family=AF_INET;
	serv.sin_port=htons(SERV_PORT);
	inet_pton(AF_INET,IPADDRESS,&serv.sin_addr);

	connect(sockfd,(SA*)&serv,sizeof(serv));

	
	epollfd=epoll_create(FDSIZE);
	add_event(epollfd,STDIN_FILENO,EPOLLIN);
	for(;;){
		ret=epoll_wait(epollfd,events,EPOLLEVENTS,-1);
		handle_events(epollfd,events,ret,sockfd,buf);	
	}
	close(sockfd);
}



void add_event(int epollfd,int fd,int state){
	struct epoll_event ev;
	ev.events=state;
	ev.data.fd=fd;
	epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
}

void hand_events(int epollfd,struct epoll_event *events,int num,int sockfd,char* buf){
	int fd;
	for(int i=0;i<num;++i){
		fd=events[i].data.fd;//process each active fd
		if(events[i].events&EPOLLIN)//the active fd is readable
			do_read(epollfd,fd,sockfd,buf);
		else if(events[i].events&EPOLLOUT)//the active fd is writable
			do_write(epollfd,fd,sockfd,buf);
	}
}


}
void do_read(int epollfd,int fd,int sockfd,char* buf){
	int nread;
	nread=read(fd,buf,MAXSIZE);
	if(nread==-1){
		perror("read error:");
		close(fd);
	}else if(nread==0){
		fprintf(stderr,"client close.\n");
		close(fd);
	}else{
		if(fd==STDIN_FILENO){
			add_event(epollfd,sockfd,EPOLLOUT);	
		}else{
			delete_event(epollfd,sockfd,EPOLLIN);
			add_event(epollfd,STDOUT_FILENO,EPOLLOUT);
		}
	}
}

void do_write(int epollfd,int fd,int sockfd,char* buf){
	int nwrite;
	char temp[100];
	buf[strlen(buf)-1]='\0';
	snprintf(temp,sizeof(temp),"%s_%02d\n",buf,count++);
	nwrite=write(fd,temp,strlen(temp));
	if(nwrite==-1){
		perror("write error:");
		close(fd);
	}else{
		if(fd==STDOUT_FILENO)
			delete_event(epollfd,fd,EPOLLOUT);
		else
			modify_event(epollfd,fd,EPOLLIN);
	}
	bzero(buf,MAXSIZE);
}

void delete_event(int epollfd,int fd,int state){
	struct epoll_event ev;
	ev.events=state;
	ev.data.fd=fd;
	epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
}

void modify_event(int epollfd,int fd,int state){
	struct epoll_event ev;
	ev.events=state;
	ev.data.fd=fd;
	epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,接下来我会详细介绍 epollepoll 是 Linux 内核提供的一种 I/O 事件通知机制,与 select 和 poll 相比,它具有更高的性能和更强的扩展性。 epoll 的特点如下: 1. 支持边缘触发和水平触发两种模式。 2. 采用基于事件驱动的方式,可以同时监听大量的文件描述符。 3. 支持添加、修改和删除事件,可以动态地改变监听的文件描述符。 4. 可以通过 epoll_wait 函数等待事件的发生,避免了轮询的效率问题。 epoll 的使用流程如下: 1. 调用 epoll_create 函数创建一个 epoll 实例。 2. 调用 epoll_ctl 函数epoll 实例添加、修改或删除事件。 3. 调用 epoll_wait 函数等待事件的发生。 4. 处理已经发生的事件。 下面是一个简单的使用 epoll 的示例: ```c #include <sys/epoll.h> int main() { int fd = socket(AF_INET, SOCK_STREAM, 0); struct epoll_event event, events[10]; int epfd, nfds; epfd = epoll_create(1); event.data.fd = fd; event.events = EPOLLIN | EPOLLET; epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event); while(1) { nfds = epoll_wait(epfd, events, 10, -1); for(int i = 0; i < nfds; i++) { if(events[i].data.fd == fd) { // 处理文件描述符 fd 的事件 } } } return 0; } ``` 在这个示例中,我们创建了一个 epoll 实例,并向其中添加了一个文件描述符,然后使用 epoll_wait 函数等待事件的发生,并处理已经发生的事件。 需要注意的是,epoll 的事件驱动模型并不是线程安全的,因此在多线程环境下需要进行同步处理。 希望这份介绍对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值