EPOLL使用的简单总结2——ET模式

注明!此篇博文全部搬运于ChinaUnix网站大佬的系列博文,主要摘自:

·彻底学会epoll(3)——ET读操作实例分析
http://blog.chinaunix.net/uid-28541347-id-4288802.html
·彻底学会epoll(4)——ET写操作实例分析
http://blog.chinaunix.net/uid-28541347-id-4296180.html
·彻底学会epoll(5)——ET模式下注意事项
http://blog.chinaunix.net/uid-28541347-id-4308612.html
·彻底学会epoll(6)——关于ET若干问题总结
http://blog.chinaunix.net/uid-28541347-id-4324338.html

0. 接着上一篇的博文,为什么要用ET不用LT。

当然很多时候也用LT(编码简单所以安全)。如果想让程序更为高效,就可以使用ET模式了。

1. ET与LT的区别

所以问题来了,ET比LT高效在哪里?
从内核内核实现分析:
在内核的实现中,俩者这是在返回时有区别。
再读操作时,
LT模式下,如果读操作就绪(内核读缓冲区有数据,EPOLLIN事件)就会把内核中的rlist拷贝一次,如果一次没有处理完读缓存的数据就还会读操作就绪(依然触发EPOLLIN事件),又会把rlist拷贝一次。
ET模式下,读操作就绪只拷贝一次(触发一次EPOLLIN事件)。

所以在LT模式下在内核拷贝的次数是n(n>=1),ET模式是1。

上篇博文讲道了性能杀手包括数据拷贝,而且还发生在内核。
所以ET模式在一定条件下比LT高效。

区别:
大体上如下面的脑图。
epoll触发模式

读操作时EP和LT触发EPOLLIN事件的区别:
http://blog.chinaunix.net/uid-28541347-id-4288802.html
写操作时ET和LT触发EPOLLOUT的区别:
http://blog.chinaunix.net/uid-28541347-id-4296180.html

·对于读取操作:
(1) 当buffer由不可读状态变为可读的时候,即由空变为不空的时候。
(2) 当有新数据到达时,即buffer中的待读内容变多的时候。
另外补充一点:
(3) 当buffer中有数据可读(即buffer不空)且用户对相应fd进行epoll_mod IN事件时。
·l 对于写操作:
(1) 当buffer由不可写变为可写的时候,即由满状态变为不满状态的时候。
(2) 当有旧数据被发送走时,即buffer中待写的内容变少得时候。
另外补充一点:
(3) 当buffer中有可写空间(即buffer不满)且用户对相应fd进行epoll_mod OUT事件时。

2. 实现过程中的区别(c++, 面向过程)

结构与上一篇博文一样,只做一些改动。
也可以看大佬的博文中的代码:
http://blog.chinaunix.net/uid-28541347-id-4324338.html

#include "myepollserver.h"

int main()
{

	int listenfd;
	listenfd = Socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); // fei zu se IO fu yong

	struct sockaddr_in serveraddr;
	bzero(&serveraddr, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
	serveraddr.sin_port = htons(8000);

	int opt = 1;
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

	Bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

	Listen(listenfd, 20);
	// clinet init date
	struct sockaddr_in clientaddr;
	socklen_t clientlen;
	int connfd;

	//epoll
	typedef std::vector<struct epoll_event> EpollList;     

	int epollfd;
	epollfd = epoll_create1(EPOLL_CLOEXEC);
	//Creates a handle to epoll, the size of which tells the kernel how many listeners there are.
	/*ET模式的下的第一处改动, epfd.events = EPOLLIN | EPOLLET ; 增加EPOLLET事件*/
	struct epoll_event epfd;
	epfd.data.fd = listenfd;
	epfd.events = EPOLLIN | EPOLLET ;

	epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &epfd);

	EpollList events(16);//You can listen for 16 at first

	int nready;

	while(1)
	{	
		nready = epoll_wait(epollfd, &*events.begin(), static_cast<int>(events.size()), -1);
		if (nready == -1)
		{
			if(errno == EINTR) 
				continue;
			perror("epoll_wait");
		}
		if(nready == 0) 
			continue;

		if ((size_t)nready == events.size())
		{
			events.resize(events.size() * 2);
		}

		for(int  i=0; i < nready; ++i)
		{
			/*ET模式的下的第二处改动,把读写操作分开后,重新确定一些变量的作用域*/
			char buf[100];
			bzero(buf, sizeof(buf));
			int n=0, nread, nwrite;
			if (events[i].data.fd == listenfd)
			{
				clientlen = sizeof(clientaddr);
				connfd = Accept4(listenfd, (struct sockaddr*)&clientaddr, &clientlen, 
									SOCK_NONBLOCK | SOCK_CLOEXEC);// fei zu se IO fu yong

				std::cout << connfd << "is come!" << std::endl;
				/*ET模式的下的第三处改动,监听与用户到来时添加EPOLLET事件,epfd.events = EPOLLIN | EPOLLET;*/
				epfd.data.fd = connfd;
				epfd.events = EPOLLIN | EPOLLET;
				epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &epfd);

			}else if (events[i].events & EPOLLIN)
			{
				connfd = events[i].data.fd;
				if (connfd < 0)
				{
					continue;
				}
				
				/*ET模式的下的第四处改动,修改读操作,一次性读完*/
				while((nread = read(connfd, buf, 100)) > 0) //read to over
				{
					n += nread;
				}
				if (n > 0)
				{	
					std::cout << "::" << connfd <<" Date: ["<< buf <<"]" << std::endl;

					/*ET*/
					epfd.data.fd = connfd;
					epfd.events = events[i].events | EPOLLOUT;
					epoll_ctl(epollfd, EPOLL_CTL_MOD, connfd, &epfd);

				}else if (n == 0)
				{
					std::cout << connfd << "is go" << std::endl;
					close(connfd);
					epfd = events[i];
					epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &epfd);
				}

			}
			/*ET模式的下的第五处改动,读写分开,一次性写完*/
			if (events[i].events & EPOLLOUT)
			{
				while(n > 0)//write ot over
				{
					nwrite = write(connfd, buf, n);
					n -= nwrite;
				}
			}
		}

	}

	return 0;

猜你喜欢

转载自blog.csdn.net/qq_40712616/article/details/85221636