#网络编程学习之epoll

本文介绍了Epoll的工作原理,包括其红黑树优化的文件描述符管理、event驱动的I/O处理方式,以及针对C10k问题的解决方案。同时,文章分析了epoll与mmap的关系、边缘触发与水平触发的区别以及线程安全问题。

1. 基本概念

我们在上一篇文章里已经讲过了改进selectpoll的两个角度,而epoll正好是实现了这两方面,因为C10k问题就可以使用epoll来解决,值得一提的是现在经常提到的不是C10k问题,而是C1000k问题,也就是百万级的并发服务器,这个问题我们应该后面会继续解决。

首先,epoll中使用红黑树来存储文件描述符结合,这使得增加、查找和修改文件描述符的时间复杂度都是O(logn)相对于select/poll有了质的提高,同时红黑树的O(logn)的修改操作使得每次只需要向内核传入待检测的fd就可以,不需要来回copy。

其次,epolleevent的缩写,也就是event驱动的poll。epoll的内部有一个wait_queue作为一个等待队列存储可以进行回调的fd,这样不需要遍历fd集合来获得可读可写的fd。

通过这两个方面的改进,epoll极大地提高了I/O多路复用的效率,也成功解决了C10K问题。

2. 代码分析

//epoll
	int epfd = epoll_create(1/*大于0即可,遗留参数*/); //int size
	//存储fd的数据结构
	struct epoll_event ev;//成员: events, data
	ev.events = EPOLLIN;
	ev.data.fd = sockfd;
	//监听套接字的fd加入内核的红黑树中
	epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
	//设置fd上限
	struct epoll_event events[1024] = {0};
	while (1) {
        //调用回调函数
		int nready = epoll_wait(epfd, events, 1024, -1);
		
		int i = 0;
		for (i = 0; i < nready; i++) {
			
			int connfd = events[i].data.fd;
            //建立服务端连接,开始监听
			if (sockfd == connfd) {
				struct sockaddr_in clientaddr;
				socklen_t len = sizeof(clientaddr);
				
				int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);
				
				printf("clientfd: %d\n", clientfd);
				//ev.events = EPOLLIN; 水平触发(默认)
				ev.events = EPOLLIN | EPOLLET;//边缘触发
				ev.data.fd = clientfd;
				epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev);
			} else if (events[i].events && EPOLLIN) {
				
				char buffer[128] = {0};
				int count = recv(connfd, buffer, 5, 0);
				if (count == 0) { //调用close()
					printf("disconnect\n");
					
					epoll_ctl(epfd, EPOLL_CTL_DEL, connfd, NULL);
					close(i);
					continue;
			   }
				send(connfd, buffer, count/*128*/, 0);
				printf("sockfd: %d, clientfd: %d, count: %d, buffer: %s\n", sockfd, connfd, count, buffer);
			}
		}
	}

3. 问题分析

针对epoll的学习,我们可以从以下三个问题入手:

1. epoll中有没有mmap,如何看出?

a. 首先在epoll源码中的__put_user()函数中可以看出内核会把当前事件copy给用户,所以没有mmap,和select/poll一样都存在copy操作。这一部分我们应该会在下一篇epoll源码分析中说明

b. 其次,从理论上理解的话。epoll是异步通信,而mmap是用来同步通信,这就需要互斥锁来解决可能的并发问题,不同的通信机制不适合。其次,mmap可能带来安全问题,epoll不适用mmap可以实现一定程度的数据隔离,避免安全风险。

2. ET 边缘触发(发送大文件时,while一次全部读完,搭配非阻塞I/O) LT 水平触发(有数据时一直触发,可以用于分包Redis)

epoll默认是水平触发,也就是当客户端连接建立之后,recv操作会循环执行直到把缓冲区的所有数据都读完毕。边缘触发则是设定一个值,每次recv操作只读该值的数据,如果没读完则在下一次epoll_wait()回调函数调用是继续读上次未完成的和新进入缓冲区的。

3. epoll是否线程安全?

a. epoll的三类基本操作:epoll_create(), epoll_ctl(), epoll_wait()是线程安全的,可以在多线程中进行访问。

b. epoll中使用红黑树存储的fd集合,如果对该集合进行操作可能是不安全的,多个线程对fd集合读写可能会发生数据竞争。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值