阻塞:函数一直不返回直到完成要做的事
非阻塞:函数直接返回 但是需要轮训来判断函数是否执行完成
阻塞和非阻塞的概念是针对底层IO操作来说的
同步: 同一个线程
异步: 使用回调函数
同步阻塞
同步非阻塞
IO多路复用
1.阻塞IO:调用者调用了某个函数,等待这个函数返回,期间什么也不做,不停的去检查这个函数有没有返回,必须等这个函数返回才能进行下一步动作
2.非阻塞IO:非阻塞等待,每隔一段时间就去检测IO事件是否就绪。没有就绪就可以做其他事。
3.信号驱动IO:信号驱动IO:linux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO时间就绪,进程收到SIGIO信号。然后处理IO事件。
4.IO复用/多路转接IO:linux用select/poll函数实现IO复用模型,这两个函数也会使进程阻塞,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检测。知道有数据可读或可写时,才真正调用IO操作函数
5.异步IO:linux中,可以调用aio_read函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。
Java
BIO 同步阻塞IO
NIO 同步非阻塞IO 底层IO是非阻塞请求,但是调用NIO的api是同步的
这里的“同步”指的就是因为在你的Java代码调用NIO接口层面是同步的,你还是要同步等待底层IO操作真正完成了才可以返回,只不过在执行底层IO的时候采用了非阻塞的方式来执行罢了
实际上,如果基于NIO进行网络通信,采取的就是多路复用的IO模型
而所谓的多路复用IO模型,就是说你的Java代码直接通过一个select函数调用,直接会进入一个同步等待的状态。
这也是为什么说NIO一定是“同步”的,因为你必须在这里同步等待某个Socket连接有请求到来。
接着你就要同步等着select函数去对底层的多个 Socket 连接进行轮询,不断的查看各个 Socket 连接谁有请求到达,就可以让select函数返回,交给我们的Java程序来处理。
select函数在底层会通过非阻塞的方式轮询各个Socket,任何一个Socket如果没有数据到达,那么非阻塞的特性会立即返回一个信息。
然后select函数可以轮询下一个Socket,不会阻塞在某个Socket上,所以底层是基于这种非阻塞的模式来“监视”各个Socket谁有数据到达的。
这就是所谓的“同步非阻塞”,但是因为操作系统把上述工作都封装在一个select函数调用里了,可以对多路Socket连接同时进行监视,所以就把这种模型称之为“IO多路复用”模型。
AIO 异步IO
业务代码提供给AIO接口一个回调函数即可
最大连接数 效率 消息传递
select FD_SETSIZE宏定义值 select()的机制中提供一种fd_set的数据结构,实际上是一个long类型的数组,每一个元素都与一个文件句柄相关联 数据由内核拷贝至用户空间
每次调用select,都需要把fd集合从用户态拷贝到内核态
内核遍历fd_set集合
poll 使用链表存储连接,没有最大限制 线性遍历 数据由内核拷贝至用户空间
epoll 上限是最大可以打开文件的数目 保证了每个fd在整个过程中只会拷贝一次 内核和用户空间使用mmap实现的共享内存,不需要拷贝
每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表,而epoll只需要检查就绪列表
一颗红黑树,一张准备就绪句柄链表,少量的内核cache
使用红黑树存储socket连接
使用双向链表作为就绪队列
使用共享内存减少用户态和内核态之间的拷贝
参考博客:
https://www.cnblogs.com/technologykai/articles/10869306.html
https://blog.csdn.net/qq_36520235/article/details/88689885