ibuv框架
从上往下看,从左往右分成网络IO与文件IO等操作:网络I/O看,linux平台通过底层epoll作为异步I/O处理,中间是抽象层uv__io_t;对于文件I/O,linux没有特定平台的异步原语,所以在线性池中执行阻塞Io来实现异步。
同步I/O
由于与cpu处理速度不匹配,我们采用多线程多进程来执行,不会当一个进程挂起后影响其他线程或进程。但是存在弊端,系统不会无上限的增加线程/进程,并且切换线程的开销也不能忽略,所以多线程多进程切换时间会占用cpu运行代码时间,降低性能,所以引入异步I/O。
异步I/O
简单来说就是,用户不需要等待内核完成实际对io的读写操作就直接返回了。
异步 事件驱动
libuv提供一套事件循环和基于I/O通知的回调函数还有loop。
libuv提供的核心工具->计时器、非阻塞网络编程(tcp)、异步访问文件系统……
loops:职责:收集事件或监控其它事件。
事件驱动编程,程序会关注事件,对于每个事件产生反应。
上图就是所有I/O事件循环处理的过程,即uv_run()函数执行过程。
1.首先判断循环是否活着,通过是否存在alive活动状态的句柄,不存在则退出。
2.开始倒计时,维护所有的定时器,若句柄超时就告知应用层,退出或者重新加入循环。
3.调用待处理的回调函数,如果有待处理的就去处理它。
4.运行空闲句柄。
5.运行准备回调句柄,在某个I/O要阻塞前,有需要的话就运行回调函数。
6.计算轮询超时,在阻塞I/O前循环会计算阻塞时间,并将I/O进入阻塞状态。
规则(先留印象,后理解):
如果使用该UV_RUN_NOWAIT模式运行循环,则超时为0。
如果要停止循环(uv_stop()被调用),则超时为0。
如果没有活动的句柄或请求,则超时为0。
如果有任何空闲的句柄处于活动状态,则超时为0。
如果有任何要关闭的句柄,则超时为0。
如果以上情况均不匹配,则采用最接近的定时器超时,或者如果没有活动的定时器,则为无穷大。
7.检查句柄回调,此时如果有可读可写的
操作就调用相应回调,如果超时了就调用超时回调。
8.如果通过调用uv_close()函数关闭,则调用close关闭。
9.在超时后更新下一次循环时间。通过UV_RUN_DEFAULT模式运行循环。
uv_run(loop, UV_RUN_DEFAULT);
读取写入是一个会降低性能的过程,与cpu速度差的很多,所以要改善这个问题。
常见的解决方式是多线程,但是libuv采用了异步的解决方法,非阻塞风格。
初始化loop前,给其分配相应的内存。在用uv_loop_close(uv_loop_t *)关闭loop后,要回收内存空间。
libuv通过创造对应的I/O设备、定时器、进程等handle来实现。
handle是不透明的数据结构,其中对应的类型uv_TYPE_t的type指定handle的使用目的。
handle(句柄)代表持久性对象。相应的handle有许多相关联的request,request是一个短暂性对象(对比于持久性就是只维持一个回调函数的时间),对映着handle的I/O操作。