一、io读写的基础原理

用户程序的IO读写程序,在大多数情况下,并没有进行实际的IO操作,而是在进程缓冲区和内核缓冲区之间直接进行数据的交换。

  1. 用户程序进行IO的读写,依赖与底层的IO,通过调用底层的read&write两大系统。
  2. read&write系统数据写入读取都是与缓冲区进行数据交换,操作系统中有许多的缓冲区,多是为了减少与设备之间的物理交换。外部设备的读写都涉及到操作系统的中断,发生中断时需要保存进程数据的状态信息,所以需要减少与物理设备的直接交换。
  3. 缓冲区的数据在达到一定的数量的时候,底层操作系统会进行IO设备的终端处理,集中执行物理设备的实际IO操作。
  4. linux系统中,操作系统内核只有一个内存缓冲区,每个用户程序(基进程)有自己独立的进程缓冲区。

重复一下结论:用户程序的IO操作,大多数情况下,都是缓冲区之间的数据交换,并不是实际的物理设备交换数据。

二、4种主要的IO模型

同步阻塞IO(BIO)
同步阻塞,client和server之间线程是1对1的关系,server线程会等待client线程写入数据,如果client不写入线程,那server线程会一直阻塞。
建立一个一对一操作,client的操作和server的操作是相互依赖的,某一个阻塞了,另一个也会阻塞等待。

同步非阻塞IO(NIO)
同步:1对1
非阻塞:client发起io请求,如果server没有准备好数据,server会直接返回错误。client会一直轮询去访问server是否准备好了数据。

  1. 在内核缓冲区没有数据的情况下,系统调用会立即返回,返回一个调用失败的信息。
  2. 在内核缓冲区中有数据的情况下,是阻塞的,阻塞直到数据从内核缓冲区复制到进程缓冲区。复制完成后,系统调用返回成功,应用程序开始处理用户空间的缓存数据。

IO多路复用(重点)
典型场景:Java NIO(new IO)、redis线程模型
下一篇文章是对Java NIO的理解Java NIO的理解
select、epoll

  1. server使用一个线程去处理客户端的请求,server和client的关系是1:n。
  2. server端复用一个线程处理多个client的请求。

异步IO(AIO)

  1. 客户端发起请求之后就做自己的事情去了,客户端会注册一个回调函数,server数据准备好之后去调用client注册的回调函数。
  2. 异步IO在windows系统下通过IOCP实现了真正的异步IO,但是在linux下,异步IO模型在2.6版本才引入,目前并不完善,底层使用的任然是epoll,与IO多路复用相同,所以在性能上面没有明显的优势。
  3. 性能上没有明显的优势,反而编程更为复杂,所以目前高并发网络应用程序的开发,大多是采用IO多路复用。
三、epoll、select、poll函数

操作系统的底层函数
poll:遍历链表,每次遍历都会线性遍历,时间复杂度为O(n),底层实现是链表,长度无限制。
select:遍历数组,每次遍历都会线性遍历,时间复杂度为O(n),底层实现是数组,长度有限制。
epoll:回调方式调用,底层实现是哈希表,使用事件通知方式,每当有IO事件就绪,系统注册的回调函数就会被调用,事件复杂度为O(1),长度是无限制的。
poll->select->epoll 主动轮询到被动回调。


李沁春
17 声望1 粉丝

喜欢运动,骑行、打羽毛球、打乒乓球等。也喜欢打桌游,玩狼人杀、剧本杀等烧脑游戏。


引用和评论

0 条评论