【IO专栏】Java IO 分析之高并发IO的底层原理【001】

大家都知道,用户程序进行IO的读写,依赖于底层的IO读写,基本上会用到底层的read&write两大系统调用。在不同操作系统中,IO读写的系统调用的名称可能完全不一样,但是基本功能是一样的。

这里涉及到一个基础的知识点:read系统调用,并不是直接从物理设备把数据读取到内存中;write系统调用,也不是直接把数据写入到物理设备。上层应用无论是调用操作系统的rad,还是调用操作系统的write,都会涉及缓冲区。具体来说,调用操作系统的read,是把数据从内核缓冲区复制到进程缓冲区;而write系统调用,是把数据从进程缓冲区复制到内核缓冲区。

也就是说,上层程序的IO操作,实际上不是物理设备级别的读写,而是缓存的复制。read&write两大系统调用,都不负责数据在内核缓冲区和物理设备(如磁盘)之间的交换,这项底层的度交换,是由操作系统内核(Kernel)来完成的。

在用户程序中,无论是Socket的IO,还是文件IO操作,都属于上层应用的开发,他们的输入(input)和输出(OutPut的)处理在编程的流程上是一致的。

 

内核缓冲区和进程缓冲区

为什么设置那么多的缓冲区,为什么要那么麻烦呢?缓冲区的目的,是为了减少频繁地与设备之间的物理交换。大家都知道,外部设备的直接读写,设计操作系统的中断。发生系统中断的时候需要保存之前的进程数据和状态等信息,而结束中断后,还有要恢复之前的进程数据和状态等信息。为了减少这种底层系统的时间损耗、性能损耗,于是出现了内存缓冲区。

有了内存缓冲区,上层应用使用read系统调用时,仅仅是把数据从内核缓冲区复制到上层应用的缓冲区(进程缓冲区);上层应用使用write系统调用时,仅仅把数据从进程缓冲区复制到内核缓冲区中。底层操作系统会对内核缓冲区进行监控,等待缓冲区达到一定数量的时候,再进行IO设备的中断处理,集中执行物理设备的实际IO操作,这种机制提升了系统的性能。至于什么时候中断(读中断,写中断)由操作系统的内核来绝对,用户程序则不用关心。

从数量上来说,在Linux系统中,操作系统内核只有个一个内核缓冲区。而每个用户程序(进程)有自己的独立的缓冲区,叫做进程缓冲区。所以,用户程序的IO读写程序,在大多数情况下,并没有实际IO操作,而是在进程缓冲区和内核缓冲区之间进行数据的交换。

 

详解典型的系统调用流程

前面讲过,用户程序所使用的系统调用read&write,他们不等价于数据在内核缓冲区和磁盘之间的交换。read把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区。具体的流程如下:

 

这里以read系统调用为例:先看一下完整的输入流程的两个阶段:

 

1.等待数据准备好

2.从内核向进程复制数据。

如果是read一个socket(套接字),那么以上两个阶段的具体处理流程如下:

第一个阶段:等待数据从网络中达到网卡。当所等待的分组到达时,它被复制到内核中的某个缓冲区。这个工作由操作系统自动完成,用户程序无感知。

第二个阶段:就是把数据从内核缓冲区复制到应用进程缓冲区。

再具体一点:如果是在Java服务器端,完成一次socket请求和响应,完整的流程如下:

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

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

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

4.服务器端返回数据:Java服务器完成处理后,构建好的响应数据,将这些数据从用户缓冲区写入内核缓冲区。这里用到的是write系统调用。

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

参考书籍《Netty、Redis、Zookeeper高并发实战》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值