基础---四种IO模型

目录

一,内核缓冲区与进程缓冲区

二,四种主要的IO模型

1.同步阻塞IO

2.同步非阻塞IO

3.多路复用IO

4.异步IO


一,内核缓冲区与进程缓冲区

    read/write操作,不是直接从用户进程到达[设备/磁盘]的,而是有一个中间商:内核缓冲区,为什么要设置内核缓冲区?

    答:cpu是时间片轮换运行的,如果线程占用cpu时间结束,则需要切换为下一个等待线程,这个时候线程的上下文切换代价高,所以有了中间商来处理,作为缓存,避免切换回来之后,重新准备内容。

    用户将数据给中间商(内存缓冲区),然后我们就解决完成了,包裹的一次发送多少,何时发送都交给操作系统来处理,也就是内核。所以我们的read就是从内核缓冲区转移到用户进程缓冲区,write操作就是从进程缓冲区转移到内核缓冲区。

    我们的内存条被分为了两个部分:(1)内核的;(2)用户的。处于内核区域的就称为内核缓冲区。

例子:java服务端完成一次socket的请求和响应具体流程:

    1.客户端请求:linux通过网卡读取客户端的请求数据,把数据读取到内核缓冲区。

    2.获取请求数据:java服务器通过read系统调用,从Linux内核缓冲区读取到数据,再送入java进程缓冲区。

    3.服务端业务处理:java服务端在自己的用户空间处理客户端的请求。

    4.服务端返回数据:java服务端完成处理后,构建好的响应数据,从用户缓冲区写入内存缓冲区,write操作。

    5.发送给客户端: linux内核通过网络IO,将内核缓冲区中的数据通过网卡,网卡通过底层的通信协议,将数据发送给目标客户端。

二,四种主要的IO模型

    阻塞非阻塞:阻塞指的是,一个函数调用,如果没有获得目标就会一直等待,常见的就是锁的获取,缓冲区中数据的获取(比如输入一个数据,scanner.in,如果用户没有输入,那么就会一直等待)而非阻塞就是不管有没有成功,都会返回一个值,比如true/false,我去访问一个东西,他还没有到达,我们就返回一个false。

    问题来了,我直接把结果返回了,不管有没有收到消息,那么消息来了怎么办,我们这里就可以间歇访问,一会又去看一下这个消息有没有来。

    同步异步:同步就是,我一个一个的完成,一路下走去,异步就会把任务派发出去,我本来要写5页作业,我找两个人来帮我写,一人写两页,我自己写一页,那么完成进度是不一致的,最后达成一个目标就是把五页作业写完。

    注意这里会产生混淆的地方,非阻塞就是异步吗?举一个例子:

    一群学生向老师问题,一个一个等待,

    1.第一个学生问老师,老师给他讲,讲明白了,学生才走(后面的都在等待)  同步阻塞

    2.学生把问题发给老师,老师收到问题后,开始解决,学生把问题发出去后就走了,去玩了,隔一会来看看老师把答案写出来没有                                                                                 同步非阻塞

    3.学生把问题给老师,学生不用跑来看老师有没有解出来,老师解出来之后,直接给学生发email。 异步非阻塞

1.同步阻塞IO

    最开始的socket编程都是同步阻塞IO,也就是BIO,

    demo:当一个read指令发起的时候,线程就会去阻塞,等待内核把内存缓冲区中的数据传输到进程缓冲区中,内核去访问特定的数据,如果数据还没有到达就需要等待,数据完整到达内核缓冲区之后,就传输到进程缓冲区,内核给线程一个返回值,比如文件的大小,然后线程才会唤醒,其中他是挂起状态,不占cpu资源的。

    缺点:一个线程对于一个socket,在高并发情况下,大量的线程上下文切换消耗资源是巨大的,无法完成高并发的任务。

2.同步非阻塞IO

    java在bio上的升级,被称为nio(None-blocking io 他不是java new io)。

    当他调用read去访问内核缓冲时,如果内核缓存中没有数据,则直接返回false,如果内核中的数据准备好了,则阻塞等待内核缓冲中的数据转移到进程缓冲中。然后唤醒线程。

    特点:当调用read一直返回false的时候,就需要轮询不断访问,直到为true

    缺点:轮询十分占用cpu资源,所以在高并发情况下也是不适用的,而我们一般所说的java nio不是这个模型,不是对于四种基本模型,而是IO多路复用模型。

    总结:同步阻塞和非阻塞的差别就在于,等待结果的过程中,是否直接返回结果,上面总结了,而同步还是异步是针对业务层面来说的。也就意味着,同步异步的层面 高于阻塞非阻塞。

3.多路复用IO

    解决问题:避免同步非阻塞的轮询消耗cpu资源问题。

    在IO多路复用模型中,引入了一种新的系统调用(基于os底层功能):“select/epoll"系统调用;通过该系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核就能够就绪的状态返回给应用程序。应用程序根进响应的就绪的状态,进行相应的IO系统调用。

    目前支持IO多路复用的系统调用,有select,epoll等,select几乎所有的操作系统都支持,epoll是在linux上提出的升级版本。

    使用该系统调用,一个线程就可以不断的轮询server上的socket连接(一个线程,就不会很占cpu了),选择器的查询,查询所有的注册的socket连接的状态,返回一个就绪的socket列表,在读取(从内核转移到用户进程的缓冲区)的时候,还是阻塞的。

    优点:一个线程专门负责进行轮询,相比传统NIO的方式,节约了很多资源,避免了大量线程在server进行切换操作

    缺点:在读取/写入的时候,还是阻塞的。

   后面章节,我们就分析该模式,也就是java new-io,他的三大组件:buffer,channel,selector。

4.异步IO

    此方式,用户线程通过系统调用,向内核注册一个io操作,内核在整个io操作(数据准备,复制到进程缓冲区中)完成之后,才通知用户线程,准备好了,线程收到通知,直接开始后续的逻辑操作,(这样就一点阻塞都没有)

    缺点:需要底层系统的支持,因为把任务都交给系统去完成了,但是Linux在2.6才引入异步操作,底层还在使用epoll,所以没有明显的优势,大家都还在使用io多路复用模型。

  具体的io方式实现,可以参考上篇关于 在线聊天系统的例子。

参考连接:https://www.jianshu.com/p/e6162bc984c8(linux网络pc端具体流程)

                 https://www.zhihu.com/question/19732473(阻塞/非阻塞/同步/异步)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值