网络IO基础知识
网络 IO
- 在套接字接口的抽象下,网络 I/O 的本质其实是 Socket 的读取
- Socket 在操作系统接口中被抽象为了数据流,而网络 I/O 就可以理解为是对流的操作
过程:两阶段
- 等待数据从远程主机到达缓冲区:从远程主机返回的数据会先存放到操作系统内核的缓冲区中
- 将数据从缓冲区拷贝到应用程序地址空间:从内核的缓冲区,复制到应用程序的地址空间
两类、五种模型
- 同步:指调用端发出请求之后,在得到结果之前必须一直等待
- 异步:在发出调用请求之后将立即返回,不会马上得到处理结果,这个结果将通过状态变化和回调来通知给调用者
以“你如何领到盒饭”这个情景类比
- 等待数据从远程主机到达缓冲区 : 等待饭做好
- 将数据从缓冲区拷贝到应用程序地址空间:将饭带回宿舍
![img](https://images.cnblogs.com/cnblogs_com/wftop1/2188260/o_220923015557_b0802b64592343179dde04619b7983fd.png)
异步 I/O(Asynchronous I/O)
你在某团外卖订了个盒饭,付款之后你自己该干嘛还干嘛去,饭做好了骑手自然会到门口打电话通知你
- 数据到达缓冲区后,不需要由调用进程主动进行从缓冲区复制数据的操作,而是在复制完成后,由操作系统向线程发送信号,所以它一定是非阻塞的
同步 I/O(Synchronous I/O)
有一个主动等待发现饭是否做好的过程,需要自己取饭
阻塞 I/O(Blocking I/O)
你去到饭堂,发现饭还没做好,你也干不了别的,只能打个瞌睡(线程休眠),直到饭做好
- 缓冲区没有数据,线程就休眠等待,直到有数据
- 优点:节省 CPU 资源
- 缺点:线程休眠所带来的上下文切换,是一种需要切换到内核态的重负载操作
非阻塞 I/O(Non-Blocking I/O)
你去到饭堂,发现饭还没做好,你就回去了,然后每隔 3 分钟来一次饭堂看饭做好了没,直到饭做好
- 缓冲区没有数据,不休眠,每隔一段时间就来查看是否有数据
- 优点:避免线程休眠,对于一些很快就能返回结果的请求,非阻塞 I/O 可以节省上下文切换的消耗
- 缺点:对于较长时间才能返回的请求,非阻塞 I/O 反而白白浪费了 CPU 资源
多路复用 I/O(Multiplexing I/O)
你是活雷锋,代表整个宿舍去饭堂打饭,去到饭堂,发现饭还没做好,还是继续打瞌睡,不过哪个舍友的饭好了,你就马上把那份饭送回去,然后继续打着瞌睡哼着歌等待其他的饭做好
- 一个线程处理多个调用的返回。有select、epoll、kqueue 等不同实现
- 优点:可以在同一条阻塞线程上处理多个不同端口的监听,CPU资源得到充分利用,且不需要挂起线程
信号驱动 I/O(Signal-Driven I/O)
你去到饭堂,发现饭还没做好,但你跟厨师熟,跟他说饭做好了叫你,然后回去该干嘛干嘛,等收到厨师通知后,你把饭从饭堂拿回宿舍
- 当缓冲区一旦有数据,就主动通知线程去缓冲区复制数据到应用程序地址空间
信号驱动 I/O与异步 I/O 的区别
- “从缓冲区获取数据”这个步骤处理,前者收到的通知是可以开始进行复制操作了,也就是你要自己把饭从饭堂拿回宿舍,在复制完成之前线程处于阻塞状态,所以它仍属于同步 I/O 操作;而后者收到的通知是复制操作已经完成,即外卖小哥已经把饭送到了。
- 信号驱动I/O是由内核通知应用程序何时开始一个I/O操作,I/O操作的执行者还是应用程序自身。而异步I/O模型是由内核通知应用程序I/O操作已经完成,I/O操作执行者是内核。
小结:
- 异步 I/O 模型是最方便
- Windows NT 内核早在 3.5 以后,就通过IOCP实现了真正的异步 I/O 模型
- Linux 系统下,是在 Linux Kernel 2.6 才首次引入,目前也还并不完善,因此在 Linux 下实现高并发网络编程时,仍然是以多路复用 I/O 模型模式为主