为什么epoll 比 select 高效

1.epoll

epool_waita函数,函数返回值nfds<=maxevents

nfds = epoll_wait(kdpfd, events, maxevents, -1);

其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait这个函数操作成功之后,epoll_events里面将储存所有的读写事件。

maxevents是最大事件数量。最后一个timeout是epoll_wait的超时,为0的时候表示马上返回,为 - 1的时候表示一直等下去,直到有事件发生,为任意正整数的时候表示等这么长的时间,如果一直没有事件,则返回。

一般如果网络主循环是单独的线程的话,可以用 - 1来等,这样可以保证一些效率,如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率。

epoll_wait范围之后应该是一个循环,遍历所有的事件:

 

nfds = epoll_wait(kdpfd, events, maxevents, -1);

for (n = 0; n < nfds; ++n)

{

if (events[n].data.fd == listener)

{

//如果是主socket的事件的话,则表示

//有新连接进入了,进行新连接的处理。

client = accept(listener, (structsockaddr*)&local, &addrlen);

if (client < 0)

{

perror("accept");

continue;

}

setnonblocking(client);//将新连接置于非阻塞模式

ev.events = EPOLLIN | EPOLLET;//并且将新连接也加入EPOLL的监听队列。

//注意,这里的参数EPOLLIN|EPOLLET并没有设置对写socket的监听,

//如果有写操作的话,这个时候epoll是不会返回事件的,如果要对写操作

//也监听的话,应该是EPOLLIN|EPOLLOUT|EPOLLET

ev.data.fd = client;

if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0)

{

//设置好event之后,将这个新的event通过epoll_ctl加入到epoll的监听队列里面,

//这里用EPOLL_CTL_ADD来加一个新的epoll事件,通过EPOLL_CTL_DEL来减少一个

//epoll事件,通过EPOLL_CTL_MOD来改变一个事件的监听方式。

fprintf(stderr, "epollsetinsertionerror:fd=%d0,client);

return-1;

}

}

else if(event[n].events&EPOLLIN)

{

//如果是已经连接的用户,并且收到数据,

//那么进行读入

intsockfd_r;

if ((sockfd_r = event[n].data.fd) < 0)continue;

read(sockfd_r, buffer, MAXSIZE);

//修改sockfd_r上要处理的事件为EPOLLOUT

ev.data.fd = sockfd_r;

ev.events = EPOLLOUT | EPOLLET;

epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd_r, &ev)

}

elseif(event[n].events&EPOLLOUT)

{

//如果有数据发送

intsockfd_w = events[n].data.fd;

write(sockfd_w, buffer, sizeof(buffer));

//修改sockfd_w上要处理的事件为EPOLLIN

ev.data.fd = sockfd_w;

ev.events = EPOLLIN | EPOLLET;

epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd_w, &ev)

}

do_use_fd(events[n].data.fd);

}


2.select

select 每次有正确返回值之后都要将全部集合查询一次,比较费时间

timeout.tv_sec = TimeoutMax;
timeout.tv_usec = 0;	 /* 设置select等待的最大时间为TimeoutMax秒加0毫秒 */
int iRet = select(maxfdp, &fds, NULL, NULL, &timeout); //select使用 maxfdp设这个值是为提高效率,使函数不必检查fd_set的所有1024位。

/*当返回为-1时,所有描述符集清0。
当返回为0时,超时不修改任何描述符集。
当返回为非0时,在3个描述符集里,依旧是1的位就是准备好的描述符。这也就是为什么,每次用select后都要用FD_ISSET的原因。
*/
if (iRet == -1)
{
	vPrtSmartDevModuleRunLog(__FUNCTION__, __LINE__, "exit() select ERR", NULL, 0, ExitLog, ExitLogBak);
	usleep(100 * 1000);
	exit(-1);
}
else if (iRet == 0) //再次轮询,当返回为0时,超时不修改任何描述符集。
{

}
else
{
	//maxevents,是已经建立的socket的数量
	for (i = 0; i < maxevents; i++)
	{
		new_fd = g_sAccepChannelBusiness[i].gAccept_fd;
		if (new_fd > 0)
		{
			int iclose = 0;//关闭对应的socket
			if (FD_ISSET(new_fd, &fds)) //测试sock是否可读,即是否网络上有数据
			{
				int n = read(new_fd, msg, MAX_BUFF);
				if (n < 0)
				{
					if (EAGAIN == errno || EINTR == errno)
					{
						continue;
					}
					_fprintf("gAccept_fd[%d] Read Error:%s  %s\n", i, strerror(errno), (char*)(inet_ntoa(g_sAccepChannel[i].gAccept_addr.sin_addr)));
					iclose = 1;
				}
				else if (n == 0)
				{
					_fprintf("TcpAcceptthread Socket Closed:%s\n", strerror(errno));
					iclose = 1;
				}
				else if (n > 0)
				{
					//将读到的数据进行处理
					DPRINTHEX("接收的数据msg=", msg, n);
				}
			}

		}
	}

}


3.

(1).epoll

nfds = epoll_wait(kdpfd, eventsmaxevents, -1);

for (n = 0; n < nfds; ++n)

(2).select

int iRet = select(maxfdp, &fds, NULL, NULL, &timeout);

for (i = 0; i < maxevents; i++)

(3).

由上面可知,在大多数的时候,nfds都是比maxevents小,所以高效一些,

但在个路io都很繁忙的时候,这两个应该就差不多,因为这个时候nfds跟maxevents相差不多。


4.

linuxepoll如何实现高效处理百万句柄的

开发高性能网络程序时,windows开发者们言必称iocplinux开发者们则言必称epoll。大家都明白epoll是一种IO多路复用技术,可以非常高效的处理数以百万计的socket句柄,比起以前的selectpoll效率高大发了。我们用起epoll来都感觉挺爽,确实快,那么,它到底为什么可以高速处理这么多并发连接呢?

 

使用起来很清晰,首先要调用epoll_create建立一个epoll对象。参数size是内核保证能够正确处理的最大句柄数,多于这个最大数时内核可不保证效果。

 

epoll_ctl可以操作上面建立的epoll,例如,将刚建立的socket加入到epoll中让其监控,或者把 epoll正在监控的某个socket句柄移出epoll,不再监控它等等。

 

epoll_wait在调用时,在给定的timeout时间内,当在监控的所有句柄中有事件发生时,就返回用户态的进程。

 

从上面的调用方式就可以看到epollselect/poll的优越之处:因为后者每次调用时都要传递你所要监控的所有socketselect/poll系统调用,这意味着需要将用户态的socket列表copy到内核态,如果以万计的句柄会导致每次都要copy几十几百KB的内存到内核态,非常低效。而我们调用epoll_wait时就相当于以往调用select/poll,但是这时却不用传递socket句柄给内核,因为内核已经在epoll_ctl中拿到了要监控的句柄列表。









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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值