三种I/O复用的比较

select,poll,epoll区别:

1、select对监听的文件描述符的个数有限制,由FD_SETSIZE的值决定为1024个,而poll和epoll都对文件描述符的和数没有限制
2、select和poll都是每次调用到时将要监听的文件描述符从用户空间拷贝到内核空间,而epoll是在内核中自己维护,不需要拷贝
3、select和poll都是用一个系统调用来完成i/o复用,而epoll是靠一组系统调用来完成的
4、select和poll在检测文件描述符上就绪的事件时是轮询来测的,而epoll是靠回调
5、select和poll返回的都是一整个数组,需要一个一个查找有事件发生的文件描述符,而epoll只返回就绪的文件描述符
6、epoll支持高效的et模式
7、select中文件描述符的表示是按位来表示的,而poll和epoll都是直接用int表示
    
poll为什么比epoll效率低:

1、poll在检测文件描述符上是否有事件发生的时候,使用的是轮询机制,和select相同;而epoll采用的是回调机制,只有事件发生的时候才会触发回调 这样不会过多的占用cpu资源
2、poll在每次调用到poll系统调用的时候都会将所监听的文件描述符从用户空间向内核空间拷贝一份,而epoll是在内核中维护了一个事件表,每次调用epoll_wait的时候,不用进行拷贝,直接开始监听,这是另外一个点
 3、epoll支持高效的et模式
4、epoll返回时只返回就绪的文件描述符,而poll会将所监听的文件描述符全部返回,要进行一个查找才能判断哪些就绪
    
epoll的et和lt模式的区别:

1、lt是水平触发,et是边沿触发
2、lt模式下,当一个文件描述符上面有事件发生的时候,如果应用程序一次没有读完数据,下次还会触发epoll_wait,而et模式下一个文件描述符上的一次就绪事件只会触发一次epoll_wait,这样就大大减少了epoll_wait被触发的次数,也是高效的一个原因。
3、et模式下必须使用非阻塞的文件描述符,由于它一个事件知乎触发一次的这种特性,这就要求我们必须一次性将数据全部读完,所以我们要用到一个 while的死循环,如果我们在循环内使用阻塞文件描述符,那么读完数据后,就会阻塞到read这里,系统就只能处理这个文件描述符上的事件,而运行不到下一次的epoll_wait处,就算其他文件描述符有事件发生,也无法处理。

可以根据这段代码来测试epoll的et模式和lt模式的不同,这是一个epoll服务器的demo,ip和port从命令行参数传入。可以自行选择lt和et模式,客户端的话建议用telnet直接连接所绑定的ip和port:

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<fcntl.h>
#include<stdlib.h>
#include<sys/epoll.h>
#include<pthread.h>
#include<iostream>
using namespace std;

#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 10

int setnonblocking(int fd)
{
	int old_option = fcntl(fd, F_GETFL);
	int new_option = old_option | O_NONBLOCK;
	fcntl(fd, F_SETFL, new_option);
	return old_option;
}

void addfd(int epollfd, int fd, bool enable_et)
{
	epoll_event event;
	event.data.fd = fd;
	event.events = EPOLLIN;
	if(enable_et)
	{
		event.events |= EPOLLET;
	}
	epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
	//setnonblocking(fd);
}

void lt(epoll_event* events, int number, int epollfd,int listenfd)
{
	char buf[BUFFER_SIZE];
	for(int i = 0; i < number; i++)
	{
		int sockfd = events[i].data.fd;
		if(sockfd == listenfd)
		{
			struct sockaddr_in client_address;
			socklen_t client_addrlength = sizeof(client_address);
			int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
			addfd(epollfd, connfd, false);
		}
		else if(events[i].events & EPOLLIN)
		{
			printf("event trigger once\n");
			memset(buf, '\0', BUFFER_SIZE);
			int ret = recv(sockfd, buf, BUFFER_SIZE-1, 0);
			if(ret <= 0)
			{
				close(sockfd);
				continue;
			}
			printf("get %d bytes of content: %s\n", ret, buf);
		}
		else
		{
			printf("somthing else happend \n");
		}
	}
}

void et(epoll_event* events, int number, int epollfd, int listenfd)
{
	char buf[BUFFER_SIZE];
	for(int i = 0; i < number; i++)
	{
		int sockfd = events[i].data.fd;
		if(sockfd == listenfd)
		{
			struct sockaddr_in client_address;
			socklen_t client_addrlength = sizeof(client_address);
			int connfd = accept(listenfd, (struct sockaddr*) &client_address, &client_addrlength);
			addfd(epollfd, connfd, true);
		}
		else if(events[i].events &EPOLLIN)
		{
			printf("event trigger once\n");
			while(1)
			{
				memset(buf, '\0', BUFFER_SIZE);
				int ret = recv(sockfd, buf, BUFFER_SIZE-1, 0);
				if(ret < 0)
				{
					if((errno == EAGAIN) || (errno == EWOULDBLOCK))
					{
						printf("read later\n");
						break;
					}
					close(sockfd);
					break;
				}
				else if (ret == 0)
				{
					close(sockfd);
				}
				else
				{
					printf("get %d bytes of content; %s\n", ret, buf);
				}
			}
		}
		else
		{
			printf("somthing else happend \n");
		}
	}
}


int main(int argc, char* argv[])
{
	if(argc <= 2)
	{
		printf("argc <= 2");
		return 1;
	}
	const char* ip = argv[1];
	int port = atoi(argv[2]);

	int ret = 0;
	struct sockaddr_in address;
	bzero(&address, sizeof(address));
	address.sin_family = AF_INET;
	inet_pton(AF_INET, ip, &address.sin_addr);
	address.sin_port = htons(port);

	int listenfd = socket(PF_INET, SOCK_STREAM, 0);
	assert(listenfd >= 0);

	ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
	assert(ret != -1);

	ret = listen(listenfd, 5);
	assert(ret != -1);

	epoll_event events[MAX_EVENT_NUMBER];
	int epollfd = epoll_create(5);
	assert(epollfd != -1);
	addfd(epollfd, listenfd, true);

	while(1)
	{
		int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, 10000);
		cout<<"epoll_wait:"<<ret<<endl;
		if(ret < 0)
		{
			printf("epoll failure\n");
			break;
		}

		et(events,ret, epollfd, listenfd);


	}
	close(listenfd);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值