epoll 与select poll区别

(1)select==>时间复杂度O(n)

它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。

(2)poll==>时间复杂度O(n)

poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的.

(3)epoll==>时间复杂度O(1)

epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))
select:

select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:

1、 单个进程可监视的fd数量被限制,即能监听端口的大小有限。

  一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。32位机默认是1024个。64位机默认是2048.

2、 对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低:

   当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。

3、需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大

poll:

poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。

它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:

1、大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。

2、poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

epoll:

epoll有EPOLLLT和EPOLLET两种触发模式,LT是默认的模式,ET是“高速”模式。LT模式下,只要这个fd还有数据可读,每次 epoll_wait都会返回它的事件,提醒用户程序去操作,而在ET(边缘触发)模式中,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无 论fd中是否还有数据可读。所以在ET模式下,read一个fd的时候一定要把它的buffer读光,也就是说一直读到read的返回值小于请求值,或者 遇到EAGAIN错误。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。

epoll接口相对于传统的select/poll而言,有以下优点:
(1)支持单个进程打开大数量的文件描述符。受进程最大打开的文件描述符数量限制,而不是受自身实现限制。而select单个进程能够打开的文件描述符的数量存在最大限制,这个限制是select自身实现的限制。通常是1024。poll采用链表,也是远超select的。
(2)Linux的I/O效率不会随着文件描述符数量的增加而线性下降。较之于select/poll,当处于一个高并发时(例如10万,100万)。在如此庞大的socket集合中,任一时间里其实只有部分的socket是“活跃”的。select/poll的处理方式是,对用如此庞大的集合进行线性扫描并对有事件发生的socket进行处理,这将极大的浪费CPU资源。因此epoll的改进是,由于I/O事件发生,内核将活跃的socket放入队列并交给mmap加速到用户空间,程序拿到的集合是处于活跃的socket集合,而不是所有socket集合。
(3)使用mmap加速内核与用户空间的消息传递。select/poll采用的方式是,将所有要监听的文件描述符集合拷贝到内核空间(用户态到内核态切换)。接着内核对集合进行轮询检测,当有事件发生时,内核从中集合并将集合复制到用户空间。 再看看epoll怎么做的,内核与程序共用一块内存,请看epoll总体描述01这幅图,用户与mmap加速区进行数据交互不涉及权限的切换(用户态到内核态,内核态到用户态)。内核对于处于非内核空间的内存有权限进行读取。

epoll实现多路IO转接思路:

	
lfd = socket();			监听连接事件lfd
bind();
listen();

int epfd = epoll_create(1024);				epfd, 监听红黑树的树根。

struct epoll_event tep, ep[1024];			tep, 用来设置单个fd属性, ep 是 epoll_wait() 传出的满足监听事件的数组。

tep.events = EPOLLIN;					初始化  lfd的监听属性。
tep.data.fd = lfd

epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &tep);		将 lfd 添加到监听红黑树上。

while (1) {

	ret = epoll_wait(epfd, ep,1024-1);			实施监听

	for (i = 0; i < ret; i++) {
		
		if (ep[i].data.fd == lfd) {				// lfd 满足读事件,有新的客户端发起连接请求

			cfd = Accept();

			tep.events = EPOLLIN;				初始化  cfd的监听属性。
			tep.data.fd = cfd;

			epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &tep);

		} else {						cfd 们 满足读事件, 有客户端写数据来。

			n = read(ep[i].data.fd, buf, sizeof(buf));

			if ( n == 0) {

				close(ep[i].data.fd);

				epoll_ctl(epfd, EPOLL_CTL_DEL, ep[i].data.fd , NULL);	// 将关闭的cfd,从监听树上摘下。

			} else if (n > 0{--write(ep[i].data.fd, buf, n);
			}
		}
	}

用法
`epoll:
int epoll_create(int size); 创建一棵监听红黑树

	size:创建的红黑树的监听节点数量。(仅供内核参考。)

	返回值:指向新创建的红黑树的根节点的 fd。 

		失败: -1 errno

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);	操作监听红黑树

	epfd:epoll_create 函数的返回值。 epfd

	op:对该监听红黑数所做的操作。

		EPOLL_CTL_ADD 添加fd到 监听红黑树

		EPOLL_CTL_MOD 修改fd在 监听红黑树上的监听事件。

		EPOLL_CTL_DEL 将一个fd 从监听红黑树上摘下(取消监听)

	fd:
		待监听的fd

	event:	本质 struct epoll_event 结构体 地址

		成员 events:

			EPOLLIN / EPOLLOUT / EPOLLERR

		成员 data: 联合体(共用体):

			int fd;	  对应监听事件的 fd

			void *ptr; 

			uint32_t u32;

			uint64_t u64;		

	返回值:成功 0; 失败: -1 errno

epoll_wait函数
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); 阻塞监听。

	epfd:epoll_create 函数的返回值。 epfd

	events:传出参数,【数组】, 满足监听条件的 那些 fd 结构体。

	maxevents:数组 元素的总个数。 1024
			
		struct epoll_event evnets[1024]
	timeout:

		-1: 阻塞

		0: 不阻塞

		>0: 超时时间 (毫秒)

	返回值:

		> 0: 满足监听的 总个数。 可以用作循环上限。

		0: 没有fd满足监听事件

		-1:失败。 errno
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值