网络编程(IO复用)

IO复用是一种机制,一个进程可以监听多个描述符,一旦某个描述符就绪(读就绪和写就绪),能够同主程序进行相应的读写操作。
本质上这些IO复用技术是同步IO技术,在读写事件就绪后需要进程自己负责进行读写,即读写过程是进程阻塞的。

与多进程和多线程相比,IO复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大见笑了系统的开销。
同步IO操作导致请求进程阻塞,知道IO操作完成,异步IO不导致请求进程阻塞。

select:允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或者多个事件发生或者经历一段时间后才能唤醒它。

int select(int maxfdpl, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);

maxfdpl:指定待测试的描述符个数,值为待测试的最大描述符加1;
接下来依次是,读描述符,写描述符,异常描述符
timeout:内核等待任意描述符就绪的超时事件,超时函数返回0,永远等待下去,值为0;等待固定一段时间;立即返回。

描述符就绪条件:
套接字可读
该连接的读半部关闭,对这样的套接字读操作将不会阻塞并返回0
该套接字是一个监听套接字,且已完成的连接数不为0
套接字上有一个错误,对其读不会阻塞并返回-1,同时把errno设为确切错误条件
该套接字接受缓冲区中的数据字节数大于等于套接字接受缓冲区低水位标记的当前大小
套接字可写
该套接字发送缓冲区中的数据字节数大于等于套接字发送缓冲区低水位标记的当前大小
该连接写半部关闭,对这样的套接字写将产生SIGPIPE信号
使用半阻塞式connect的套接字已建立连接,或者connect以失败告终
套接字上有有个错误,对其写将不会阻塞并返回-1,同时把errno设为确切错误条件

poll

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

fdarray:指向pollfd数组的指针,每个pollfd结构包含了描述符及其相应事件。
ndfs:数组元素个数。
如果不关心某个特定描述符,可以把与之对应的pollfd结构的fd成员设置成一个负值,poll函数将忽略这样的pollfd结构的events成员,返回时将其revents成员的值置为0;

epoll

#include <sys/epoll.h>
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);

epoll_create():创建一个epoll句柄,它会占用一个fd,用完epoll后,必须将其关闭。
参数:
size:告诉内核监听描述符的数量,并不是监听数量的最大值,是对内核初始分配内部数据结构的一个建议
返回值:常见epoll的句柄

epoll_ctl():对描述符fd进行op操作。
op:操作
EPOLL_CTL_ADD:注册新的fd到epfd中
EPOLL_CTL_DEL:从epfd中删除一个fd
EPOLL_CTL_MOD:修改已注册fd的监听事件
fd:操作的描述符
event:告知内核需要监听的时间。
EPOLLIN:对应的描述符可读
EPOLLOUT:对应的描述符可写
EPOLLPRI:对应的描述符有紧急数据可读(带外数据)
EPOLLERR:对应的描述符发生错误
EPOLLHUP:对应的描述符被挂断
EPOLLET:将epoll设为边缘触发模式(默认为水平(LT)触发模式)
EPOLLONESHOT:只监听一次事件,监听完后,如果需要再次监听,需再次将描述符加入到epoll队列
epoll_wait():等待epoll句柄上的I/O事件,最多返回maxevents个事件
参数:
epfd:epoll_create得到的epoll句柄。
events:从内核得到事件的集合
maxevents:返回事件的最大数量(不能大于创建epoll句柄时的size参数)
timeout:超时参数

工作模式:
LT水平触发模式:
应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次向应用程序通知此事件
边缘触发(ET)模式
应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不再向应用程序通知此事件。

扫描二维码关注公众号,回复: 10185274 查看本文章

select,poll,epoll比较
时间复杂度
select–O(n):采取无差别轮询,找出能读的数据流以及写数据流
poll–O(n):将用户传入的数组拷贝到内核空间,然后查询每个描述符状态,无最大连接限制
epoll–O(1):哪个流发生了怎么样的IO事件通知

优缺点:
select
单个进程可监视fd数量受限
对socket的扫描是线性的,即采用轮询方式
需要维护一个用来存储大量fd的数组结构,使得用户空间和内核空间在传递该结构时,复制开销大

poll
与select类似,将用户传入的数组拷贝到内核空间,然后查询每个fd的状态,如果设备就绪,则在设备等待队列中 加入一次并继续遍历,若遍历完该数组没有发现就绪设备,则挂起该进程直到设备就绪或者主动超时。
没有最大连接限制(基于链表存储)
大量的fd数组被整体复制到用户态和内核态,而不管它们是否有意义
水平触发模式,如果这次报告的fd没有被处理,那么下次poll仍会报告

epoll
LT模式:只要fd可读,每次epoll_wait都会返回它的事件,提醒用户操作
ET模式:只会提醒一次,除非有新数据流入,所以在ET模式下,读一个fd,一定要读光所有的buffer
epoll使用事件的就绪通知方式,通过epoll_ctl注册fd,一旦fd就绪,内核就会采用类似于回调函数机制来激活fd。
无最大并发连接限制
效率提升(非轮询模式),只在意活跃的fd,而与连接总数无关。
内存拷贝利用mmap文件映射内存,加速与内核空间消息传递。

发布了39 篇原创文章 · 获赞 10 · 访问量 749

猜你喜欢

转载自blog.csdn.net/weixin_43393776/article/details/100664957