阻塞IO和非阻塞IO,同步IO和异步IO

一、阻塞io

我这个进程调用了一个功能需要磁盘io,那么我整个进程就会被阻塞住,在做完磁盘io之前我都不能动。当内核把数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用 户线程,用户线程才解除block状态。

二、非阻塞io

我调用了一个磁盘io,但是我不用等他io完,我可以去进行别的东西,只要他io完就会通知我去读取数据;

三、同步IO

非阻塞io在进行磁盘io的时候,虽然不需要等磁盘io这个过程,但是当磁盘io完成之后,他还需要把数据从内核空间移动到用户空间,这个时间也是阻塞的,这就是同步.

四、异步IO

我调用了磁盘io,我会创建一个新的线程去处理这整个io,我只需要调用read,随后发生的什么时候,我创建了一个新的线程去帮我处理,等他处理完,我再回来接收已经准备好的数据.完全不用花费时间在等待,可以做别的事情。

 

这里粗略地举个栗子,比如你要玩英雄联盟。

阻塞IO就是,游戏登录进去之后你要先匹配队友磁盘IO,匹配完队友后,你还要自己玩排位把数据从内核复制到用户空间。而非阻塞IO就是你下课回到宿舍,让你室友先帮你排队磁盘IO,你先去干点别的事情,比如上厕所,等你干完别的时候回来,发现游戏已经进入了,你可以直接选用英雄开始打了。同步IO就是虽然你让你室友帮你匹配了队友,但是你还是得自己打游戏才能上分把数据从内核复制到用户空间,这个时间段你也无法做别的事情。而异步IO就是,你请一个代练,你只需要告诉他,我要上王者100点,那么他就会把所有事情都做得明明白白磁盘io并且把数据从内核复制到用户空间,你拿到手的时候就已经是一个段位为王者的号了。

 

五、Socket

 

1.文件标识符fd

 

 

每一个程序都有一个基础流,文件标识符0、1、2,输入流、输出流、错误流。如果这个进程监听了一个端口,那就会多一个3,是socket

2.当服务端监听了某个端口

只有监听了端口才会有3socket

3.当客户端与服务端链接时

服务端的文件标识符会变成4个,3是服务端监听了一个端口,就会创建这个socket,当客户端跟服务端链接的时候,就会多了一个socket,表示已经跟客户端建立了链接

 

4.看源码

nc localhost 8080 (建立连接的命令行)

read() 读文件标识符

accept() 当客户端想要跟你连接时,接收到一个fd,然后建立链接

六、IO的内存模型

 

 

时间片:当时间到了,调用进程调度,回调collBack,切换另一个进程

 

所以进程越多,对CPU的性能压力越大

 

切换的方式是利用晶振的规律震动,这里可以扩展到Redis的知识点,Redis是单线程, 采用单线程,避免了不必要的上下文切换.

七、IO的发展

1.阻塞模型

 

 

 

如果fd4在read()阻塞了,此时如果有另外一个fd5,即使建立了连接,但是因为scoket在fd4阻塞住了.

[缺点]:会发生阻塞

2.抛出线程,阻塞在线程

 

 

但是仍然有缺点,因为线程多了起来,线程的切换也需要消耗很大性能

3.非阻塞I/O(NIO)

 

 

只需要设置一下read,调用了read函数之后,不需要等待磁盘io,它会先返回一个结果,这样子不会阻塞,也不需要抛出线程.减少了线程的切换.

但是仍然还有缺点:因为即使进程不需要等到磁盘io,但是磁盘读取完数据之后,我们还是需要把数据从内核空间复制到用户空间,这样子也会消耗性能.(因为read是对系统调用的方法)并且,如果有一万个客户端发来信息,但是我们每次去调用read的时候,是需要遍历1万个fd,才能发现哪个fd有事件(event),这是一个时间复杂度特别高的事情.而且一万个fd里面可能只有几个fd有事件,这就造成了浪费.

4.多路复用

在内核上进行优化,因为我们之前每次都是要把fd传到内核空间,然后再调用read判断它有没有事件,所以我们包装了一个select(),假设有一万个fd,我们一次性把fd传到内核里面,内核把这一万个fd遍历一遍,然后再把有事件的fd返回出去.在这里read只需要调用有事件的fd次数,比如只有5个fd有事件发生,那么read只需要调用5次.

 

 

但是仍然还有缺点,因为每一次调用read,都要在内核空间里面循环一万个fd,查看有没有事件(select),而且每次都要把fds这个集合复制到内核空间,这样子也会浪费资源.

5.epoll

 

所以我们在内核空间划出一块空间,把整个fds集合放在内核空间里面,这样子每此调用read的时候就不用涉及到内核态和用户态的切换了.

客户端数据到达就会产生中断,然后查找一下这个终端号是那个fd的,然后返回给read就可以了

 

 

 八、NIO

1.特点

①、是基于流的形式,但又采用了缓冲区和管道来处理数据的;

 

 

②、NIO是双向的;

③、NIO是使用多路复用的IO模型.

2.管道是什么?

通道是对原 I/O 包中的流的模拟。到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象(通道)。一个 Buffer 实质上是一个容器对象。发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。 Channel是一个对象,可以通过它读取和写入数据。

3.缓冲区(buffer)

4.selector

Selector运行单线程处理多个Channel

 

posted @ 2020-10-29 12:55  拿着放大镜看世界  阅读(832)  评论(0编辑  收藏  举报