后台开发核心技术(13):select、poll、epoll

前面文章中说了一些关于IO多路复用的知识,今天专门总结一下select、poll、epoll的用法和区别;
IO复用有时也称为事件驱动,基本原理就是:有一个函数会不断轮询所负责的socket,当某个socket有数据到达了,就通知用户进程。所以称之为事件驱动!
这里的函数就是用select、poll、epoll来实现轮询的功能。

select

select在socket编程中还是相当重要的,可是很多初学者并不爱用select写程序,习惯直接用connect、accept、recv或recvfrom这样的阻塞程序。使用select就可以完成非阻塞方式工作的程序,它能够监视需要被监视的文件描述符的变化情况——读、写或异常。

函数原型

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval*timeout);

这里用到了两个结构体,fd_settimeval
1、结构体fd_set可以理解为一个集合,这个集合中存放的是文件描述符,即文件句柄,这可以认为是常说的普通意义文件,socket就是一个文件(在linux中),所以socket句柄就是一个文件描述符。
2、结构体timeval是一个常用得结构,用来代表时间值,有两个成员,一个是秒数一个是毫秒数。
3、maxfdp是一个整数值,是指集合中所有文件描述符得范围,即最大得文件描述符加1。
4、readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符。因为要监视文件描述符是变化的,即关心是否可以从这些文件中读取数据,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读。如果没有可读的文件,则根据timeout参数再判断是否超时:若超出timeout的时间,select返回0;若发生错误则返回负数;
5、writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符。因为要监视文件描述符是可写变化的,同样与readfds一样,返回值代表含义:大于0——有可写,等于0——超时,小于0——错误。
6、errorfds同上面两个参数的意图,用来监视文件错误异常。
7、timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态:第一种是若将NULL以形参传入,则不传入时间结构,就是将select置于阻塞状态,一定要等待监视文件秒描述符集合中某个文件描述符发生变化为止。第二种是将时间设置为0,就变成了一个纯粹的非阻塞函数,不管描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正数。第三种,timeout大于0,就是等待的超时时间,即select在timeout时间内阻塞,超时时间内有时间到来就返回了,否则在超时后不管怎么样都返回,返回值同上。

poll

和select一样,poll也可以用于执行多路复用IO。函数原型:

int poll(struct pollfd * fds,unsigned int nfds,int timeout);

pollfd结构体的定义如下:

struct pollfd
{
   int fd;  // 文件描述符
   short events;  // 等待的事件
   short revents;  // 实际发生了的事件
};

每一个结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示poll()监视多个文件描述符。每个events域是监视该文件描述符的事件掩码,由用户来设置这个域的属性,revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域。
详细看一下revents事件的事件分类:

事件分类事件代码意义
合法事件POLLIN有数据可读
合法事件POLLRDNORM有普通数据可读
合法事件POLLRDBAND有优先数据可读
合法事件POLLPRI有紧迫数据可读
合法事件POLLOUT写数据不会导致阻塞
合法事件POLLWRNORM写普通数据不会导致阻塞
合法事件POLLWRBAND写优先数据不会导致阻塞
合法事件POLLMSGSIGPOLL消息可用
非法事件POLLER指定的文件描述符发生错误
非法事件POLLHUP指定的文件描述符挂起事件
非法事件POLLNVAL指定的文件描述符非法

timeout参数和select一样,返回值也一样,但是这里当返回值为负数时(timeout为大于0时),只返回-1,并设置errno为下列值之一:

EBADF一个或多个结构体中指定的文件描述符无效
EFAULTfds指针指向的地址超出进程的地址空间
EINTR请求的事件之前产生一个信号,调用可以重新发起
EINVALnfds参数超出PLIMIT_NOFILE
ENOMEM可用内存不足,无法完成请求

epoll

epoll是在Linux2.6内核中提出的,是之前select和poll的增强版本,相对于select和poll来说,epoll更加灵活,没有描述符限制,epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间之间的数据拷贝只需要一次。

epoll接口
首先使用epoll必须包含 #include <sys/epoll.h>
epoll操作过程需要3个接口,分别如下:

int epoll_create(int size);
int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);

下面分别介绍这三个接口的功能、入参和出参的含义:

int epoll_create(int size);

创建一个epoll句柄,size用来告诉内核要监听的数目,这个参数不同于select中的第一个参数,是最大监听的fd+1的值。当创建好了epoll句柄后,就会占用一个fd的值,所以在使用完epoll后,一定要close()关闭,否则可能导致fd被耗尽。

int epoll_ctl(int epfd,int op,int fd, struct epoll_event *event);

epoll的事件注册函数, 它不同于select()在监听事件时告诉内核要监听什么类型的事件,而是先注册要监听的事件类型,
第一个参数是epoll_create()的返回值;
第二个参数表示动作,用三个宏来表示:EPOLL_CTL_ADD,注册新的fd到epfd中;EPOLL_CTL_MOD:修改已经注册的fd的监听事件;EPOLL_CTL_DEL:从epfd中删除一个fd。
第三个参数是需要监听的fd;
第四个参数是告诉内核要监听什么事,strcut epoll_event结构如下:

strcut epoll_event
{
   _uint32_t events;   // epoll监听的事件
   epoll_data_t data;  // 用户数据可变
};

events可以是以下几个宏的集合:EPOLLIN,表示对应的文件描述符可以读;EPOLLOUT,表示对应的文件描述符可以写;EPOLLPRI,表示对应的文件描述符有紧急数据可读;EPOLLERR,表示对应的文件描述符发生错误;EPOLLHUP,表示对应的文件描述符被挂断;

int epoll_wait(int epfd,struct epoll_event * events, int maxevents, int timeout);

等待事件的发生,类似于select()调用,参数events用来从内核得到事件的集合,maxevents告诉内核这个events有多大,且maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间。

总结

select、poll、epoll的异同:
同:select、poll、epoll都是多路IO复用的机制,多路IO复用就通过一种机制,可以实现多个描述符,一旦某个描述符就绪(一般是读写),能够通知程序进行相应的读写操作。但是select、poll、epoll本质上都是同步IO,因为它们都需要在读写事件就绪后自己负责读写,即是阻塞的,而异步IO则无需自己负责读写,异步IO的实现会负责把数据从内核空间拷贝到用户空间!
异:
1、首先看一下select和poll:

1、poll()在应付大数目的文件描述符的时候更快,因为对于select来说内核
需要检查大量描述符对应的fd_set中的每一位比特,比较费时。

2、select监控的数量是固定的,相对来说较少(1024或2048),如果需要监听
数值比较大的描述符效率很低,而poll可以创建特定大小的数组来保存监控的描述
符,而不受文件描述符值的大小限制,而且poll的监控文件数量远大于select。

3、select的超时参数在返回时是未定义的,考虑到可移植性,每次在超时之后再
下一次进入到select之前都需要重新设置超时参数。

4、select的可移植性更好,在某些UNIX系统上不支持poll。select对于超时值提供了更好的精度。

下面对epoll单独介绍一下它的强大之处!

第一:支持一个进程打开最大数目的socket描述符;
select最大的缺点就是一个进程打开的fd有限制,由FD_SETSIZE的默认值1024/2048。对于那些需要支持上万链接数量的服务器来说显然不够。这时候可以选择修改这个宏然后重新编译内核。不过epoll没有这个限制,它所支持的FD上限是最大可以打开的文件的数目,这个数目一般远大于2048,例如,1G内存空间中这个数字一般是10w左右。
第二:IO效率不随FD数目增加而线性增加!
传统的select/poll另一个致命的弱点就是当你拥有一个很大的socket集合,不过由于网络延迟,任一时间只有部分的socket是活跃的,但是select/poll每次调用都会线性扫描全部集合,导致效率呈线性下降。但是epoll不存在这个问题,它只会对活跃的socket进行操作——这个是因为在内核中实现epoll是根据每个fd上面的callback函数实现的。那么只有活跃的socket才会主动去调用callback函数,在这一点上,epoll实现了一个伪AIO,因为这时候推动力由Linux内核提供。
第三:使用mmap加速内核与用户空间的消息传递。
这点实际上涉及epoll的具体实现,无论是select、poll还是epoll都需要内核把fd消息通知给用户空间,如何避免不必要的内存拷贝就非常重要了,在这里epoll是通过内核与用户空间mmap处于同一块内存实现的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
后台开发核心技术与应用实践 作者:徐晓鑫 著 出版日期:2016年08月06日 封面宣传语:腾讯云平台技术总监黄世飞、Facebook对外支付项目主程张子兴、微软软件工程师彭可竞、阿里巴巴资深算法工程师周乐、百度大数据高级测试工程师畅晋联袂推荐;围绕后台开发需要掌握的核心技术,从多个方面、多个角度进行了阐述,覆盖了该领域的几乎所有内容;充分抓住本质并结合实践,文字通俗易懂,可操作性强 出版书名:后台开发核心技术与应用实践 作者:徐晓鑫 著 封底文字 专家评价 后台开发是一个“历史悠久”的领域,同时也是一个沉淀深厚,高技术价值的领域。本书清晰、严谨、务实的风格显示出晓鑫对该领域知识的深刻理解。 ——张子兴 Facebook对外支付项目主程,美国加州MenloPark 每一位从事后台开发的专业人士都需要一本后台开发指南。对每一位想要认真从事该领域工作的人来说,本书是一本绝对必读的书籍。 ——彭可竞 微软软件工程师,美国华盛顿州Redmond 本书是作者多年后台开发、架构和研究的精华。书中用通俗的文字、详尽的示例代码,结合实际工作中的案例,讲述了后台开发方方面面的知识,内容丰富。对于从事后台开发的人员,这是一本很好的由浅入深的学习书籍。 ——周乐 阿里巴巴资深算法工程师,北京望京 使用C++语言进行后台开发有一定的门槛,本书可以很好地帮助你跨过这个“门槛”。 ——畅晋 百度大数据高级测试工程师,北京上地 前勒口 互联网网民日益剧增,各种应用层出不穷,各项技术更新不断。单是游戏行业,近几年就经历了从端游、页游到手游的巨大变迁,客户端更新迭代之快,始料未及。而后台开发中使用到的技术,却变化不是很大。让服务性能更高、处理能力更强、安全性更好,是后台开发工程师永恒的主题。 后台开发中用到的技术,深而广,需要读的“大部头”很多,光是Richard Stevens的APUE,UNP,TCP/IP详解就够读个半年以上。读者通过阅读本书,可以从实践出发,快速由浅入深地进入后台开发领域。在读完本书,有了实践的经验之后,再去阅读大师们的著作,会更有体会,更懂得如何欣赏。 读书的最高境界莫过于“把书读薄,把书读厚”。本书文字通俗易懂,让你更快地“读薄”,同时又涉及较多的核心知识点,顺着这些知识点,读着读着也发觉“读厚”了。 后勒口 徐晓鑫,腾讯资深软件研发工程师,先后在腾讯游戏之洛克王国、QQ会员、QQ秀等项目工作,精通后台开发各种技术,实战经验丰富。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值