同步和异步:关注点在于调用方是否主动获取结果
同步:调用方需要主动等待结果返回;
异步:不需要主动等待结果返回,通过其它手段获取结果,如回调函数;
阻塞和非阻塞:关注点是等待结果返回调用方的状态;
阻塞:是指结果返回前,当前线程呗挂起,不做任何事;
非阻塞:指结果返回前,线程可以做其它事情;
(*好好品味上述描述)
组合态的类型:
同步阻塞:最常见模型,去买东西,东西卖完了,你在店里一直等待,不做任何事情(干等),等到有货位置;效率很低;
同步非阻塞:同步非阻塞可以抽象为轮训模式,去买东西,东西卖完了,不需要傻傻的等着,你可以去买奶茶,但这期间你需要时不时的去问老板东西到货了吗;
异步阻塞:这个不常用,类似于你去买东西,东西卖完了,你把手机号留给老板,然后傻傻的什么也不做,等着老板告诉你有货了;
异步非阻塞:好比你去买东西,东西没了,你把手机号告诉老板,衣服到了联系你,这期间你可以去做任何事情;
a、阻塞I/O
b、非阻塞I/O
c、I/O复用(select、poll和epoll)
d、信号驱动I/O
d、异步I/O
总的来说,阻塞 IO 就是 JDK 里的 BIO 编程,IO 复用就是 JDK 里的 NIO 编程,Linux 下异
步 IO 的实现建立在 epoll 之上,是个伪异步实现,而且相比 IO 复用,没有体现出性能优势,使用不广。非阻塞 IO 使用轮询模式,会不断检测是否有数据到达,大量的占用 CPU 的时间,是绝不被推荐的模型。信号驱动 IO 需要在网络通信时额外安装信号处理函数,使用也不广泛。
阻塞I/O模型
I/O复用模型
比较上面两张图,IO 复用需要使用两个系统调用(select 和 recvfrom),而 blocking IO 只
调用了一个系统调用(recvfrom)。但是,用 select 的优势在于它可以同时处理多个 connection。
所以,如果处理的连接数不是很高的话,使用 select/epoll 的 web server 不一定比使用
multi-threading + blocking IO 的 web server 性能更好,可能延迟还更大。select/epoll 的优势
并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
select、poll、epoll是Io多路复用机制,IO多路复用机制是一个进程可以监视多个描述符,一单某个描述符就绪(读或写就绪),能够通知响应程序进行响应的读写操作;但select、pollepoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,并等待读写完成;
在linux上,可将一切都看做是文件,包括普通文件、目录文件、字符设备文件、快设备文件、套接字等;所有的一起都抽象成文件,提供统一接口,方便程序调用;
如程序需要打开或者新建一个文件时,内核会返回一个文件描述符用于打开或者新建文件,本质是一个肺腑整数,实际是一个索引值,指向内核为每个进程所维护的该进程打开文件的记录表;
select:函数监视的文件描述符分为三类,writefds、readfds和exceptfds。调用后select函数会阻塞,直到描述符就绪,或者超时返回空;当select函数返回后可以通过遍历fdset,来找到就绪的描述符;
int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval
*timeout);
select 目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select 的一 个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在 Linux 上一般为 1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低。
提高效率的关键点在于将fd文件给内核处理;
缺点:1 bitmap只有1024大小,变大影响效率
2 存储fd的set不可重复利用
3 内存copy有较大的开销
4 内核判断是否有数据后,select又判断了一次
poll:不同与 select 使用三个位图来表示三个 fdset 的方式,poll 使用一个 pollfd 的指针实现。pollfd 结构包含了要监视的 event 和发生的 event,不再使用 select“参数-值”传递的方
式。同时,pollfd 并没有最大数量限制(但是数量过大后性能也是会下降)。 和 select 函数
一样,poll 返回后,需要轮询 pollfd 来获取就绪的描述符。
int poll (struct pollfd *fds, unsigned int nfds, int timeou
poll与select最大的区别在于采用pollfd存储fd文件,消除了bitmap不可重复利用的问题,并且数组存储大小不会局限于1024;
epoll:不同与 select 使用三个位图来表示三个 fdset 的方式,poll 使用一个 pollfd 的指针现。pollfd 结构包含了要监视的 event 和发生的 event,不再使用 select“参数-值”传递的方式。同时,pollfd 并没有最大数量限制(但是数量过大后性能也是会下降)。 和 select 函数一样,poll 返回后,需要轮询 pollfd 来获取就绪的描述符。
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解决了select的几个问题:
1 bitmap只有1024大小,变大影响效率
2 存储fd的set不可重复利用
3 内存copy有较大的开销
4 内核判断是否有数据后,select又判断了一次
(未完持续更新中。。。。)