I/O阻塞非阻塞与异步 epoll总结 ET/LT AStar提速

来源:互联网 发布:做淘宝美工怎么样 编辑:程序博客网 时间:2024/06/05 04:22

参考

https://segmentfault.com/a/1190000003063859

http://blog.chinaunix.net/uid-17299695-id-3059078.html


个人笔记记录



内核空间与用户空间
linux操作系统将虚拟空间(32位4G)分为内核空间和用户空间,为了安全,保证用户进程不能直接操作内核
内核可以访问受保护的内存空间,访问底层硬件设备 内核管理进程的切换,很耗费资源的。保存切换上下文,程序计数器,寄存器


进程的阻塞:


阻塞态:等待一件事情(I/O设备)暂时不能运行的状态,处理机空闲,进程却不使用
请求的资源得到满足或者等待的条件的到满足,就从阻塞态变为就绪态,等待CPU优先级进入运行态


进程的组成
程序 数据 PCB(process control block进程控制块)
PCB包括:
进程标识、进程状态(就绪阻塞运行)、
进程实体(进程的程序和数据在内存中的位置和大小)
调度信息(就绪转为运行的优先级)
资源信息(描述内存占用、外设占用的信息)
现场信息(程序计数器、程序状态字、累加器、变址寄存器的当前值)
进程通信信息(进程间通信)


系统自动执行阻塞原语,自己由运行状态变为阻塞状态,进程的阻塞是进程自身的一种主动行为
只有处于运行状态,然后等待什么才会进入阻塞态,进程进入阻塞态不占用CPU资源


文件描述符:fd就是一个索引,比如一个socket,一个epoll 都有自己的fd
缓存IO:I/O数据先拷贝到操作系统内核的缓冲区,然后从内核的缓冲区拷贝到用户程序的地址空间
数据传输过程中需要在应用程序空间和内核进行多次数据拷贝操作,CPU占用和内存开销是非常大的


因此基于以上的I/O,socket的read操作包括两个阶段:
1等待数据准备
2拷贝数据从内核到进程


有5种模式来做这件事情
阻塞IO
非阻塞IO
IO多路复用(tcpmultipluxor  select epoll)
异步IO


multipplexor
一个进程同时处理多个socket,监听轮询多个socket,有某个数据到达,就通知用户进程
调用了select或者epoll_wait那么整个进程就阻塞住了
内核会监视这些socket,哪一个数据准备好了,select就返回。read的第一步完了,然后把数据从内核拷贝到用户空间完成
多路复用就是一个东西监视多个scoket,不需要每个每个单独处理
select epoll_wait都是阻塞看内核中哪个socket好了,然后对应的连接去read从内核读
因此这两个只是监听内核中fd有没有准备好,后面还需要在读,因此比单个直接阻塞着好了然后读其实浪费时间
因为多了个select epoll_wait的返回,这俩返回准备好的fd,然后我们再去读。
优点在于:多路复用,同时处理多个连接
连接数如果不高,selectepoll并没有多线程 阻塞IO的性能好,但是连接更多,几千几万,那么selectepoll就很好用了
IO多路复用中,一般socket都是设置为nonblocking,但是select epoll_wait是阻塞的这没办法




阻塞非阻塞IOMultiplexor都属于同步
操作的时候回阻塞进程
异步则是:注册事件,发生了,直接调用注册时候的回调函数。类似月C# AS中的addEventListener
主进程是根本不管这些操作的,函数的执行也是由回调来执行
select:监视fdset个数有限,而且遍历,效率不好
epoll:每个连接整了注册事件放进去,返回一个events[],当哪个有动静,然后直接就能找到哪个fd的连接
无描述符1024限制,将用户关心的文件描述符的事件放到内核一个 事件列表 ,用户空间内核空间只拷贝一次


epoll事先通过epoll_ctl()来注册一 个文件描述符,一旦基于某个文件描述符就绪时,
内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait() 时便得到通知。
此处去掉了遍历文件描述符,而是通过监听回调的的机制。这正是epoll的魅力所在。
连接大量,空闲连接多,epoll优于select



AStar算法的优化


openlist每次排序最小作为新的currentNode是最耗费时间的
所以,每次openlist插入新的节点,删除节点,节点替换(f值更小,更新parentNode)
这三种操作的时候,如果能保证有序,然后每次直接取openlist[0]作为currentNode即可
这样会省去很多计算。
关键注意,并不一定保证完全有序,我们只需要的是第0位最小即可,因此可以2叉树的半个用法即可


空间换取时间,所有地图节点全部存到一个二维数组。openlist中的节点都不是new出来的
都是从地图节点放进来的。
每次放进openlist中的点,第二次AStar的时候把这些点reset下,不然new的开销是很大的。


加速方法:
1 空间换时间,不要每次new,先全部生成节点。AStar的时候光往openlist里面放
2 openlist每次取第0位,插入,删除,替换 二叉树放到正确位置,不需要排序
3 H值得计算,1.4x+(y-x).G值计算,拐弯变方向加代价



ET/LT总结

误区:
一般认为lt模式在epoll池中存在大量的fd效率显著低于et模式
实际上,从kernel看两者处理逻辑几乎相同,差别在于lt模式在event发生时不会将其从ready_list中移除
略微增大了event处理过程中kernelspace中记录数据的大小
然而et模式一定要配合应用层的ready_list,以便收集已出现的event的fd,在通过round_robin(取余轮训)挨个处理
宾冕通信数据量很大时,忙于处理热点fd导致非热点fd饿死
ready_list实现千奇百怪,不一定经过仔细推敲优化,所以et的总内存开销往往大于lt
ET能减少相关epoll的系统调用,为绕过饿死问题,ET中的userapp自行进行read/write循环处理,
这区中增加的系统调用和减少的epoll系统调用加起来,鹿死谁手尚未可知


总结
epoll_wait效率是0(ready fd num)级别,ET模式优势在于减少每次epoll_wait可能返回的fd数量,
并发event数量极多的情况下能加快epoll_wait的处理速度,但是这只是epoll体系自己而言
与此同时,userapp层面需要增加ready_list等复杂的逻辑,话费更多的cpu/mem与其配合工作
总体性能究竟如何需要实际测试才知道。
为了降低处理逻辑复杂度,常用的时间处理库大都选择了LT模式 libevent boost::asio等
ETLT模式处理逻辑差异很小,ET在userapp层面逻辑复杂出错概率高,
性能差异主要在于epoll_wait的系统调用的处理速度。
是否是userapp的性能瓶颈需要视应用场景而定,不可一概而论

原创粉丝点击