linux网络IO

一、内核空间与用户空间

1、虚拟地址空间

多任务操作系统中,每个进程都运行在属于自己的内存沙盘中,就是虚拟地址空间,虚拟地址空间由内核空间和用户空间两部分组成

虚拟地址会通过页表映射到物理内存,页表由操作系统维护并被CPU引用,每个进程都有自己的页表

2、内核空间

内核空间在页表中需要较高特权级才能访问,进程处在用户态时访问这些页会被阻止

内核空间是持续存在的,并且在所有进程中都映射到同样的一段物理内存(linux中大约3~4G),而用户空间的映射则随进程的切换不断变化

所有的系统资源管理都是在内核空间中完成的,比如读写磁盘文件,分配回收内存,从网络接口读写数据等

3、特权级(内核态、用户态)

Intel x86体系结构的CPU架构中定义了Ring0~Ring3这4个特权级,Ring0最高,不过Linux和Windows其实只使用了Ring0和Ring3两个级别

Linux中用Ring0表示内核态,Ring3表示用户态

4、为什么要划分内核空间与用户空间

CPU的所有指令中,有些指令是非常危险的,如果错用会导致系统崩溃,比如清内存指令、时钟指令等,为了保证系统的稳定运行,这类指令被归为特权指令,只能在内核空间运行,被处在内核态的进程调用

而逻辑运算指令、算数指令这种不影响其他用户和系统的指令归为非特权指令,没有运行和调用的限制

5、用户态和内核态的切换

程序的进程大部分是运行在用户态下的,在需要调用硬件或者运行与系统核心相关的内容时,会切换到内核态

以open()文件打开函数为例,应用在用户空间执行到API函数open()时,会触发系统软中断,系统会调用sys_open()这个系统调用函数,在内核空间执行open代码,这样用户空间的open函数内部代码就取得了内核空间运行的权限,进程切换到了内核态,可以执行一些特权指令,open代码执行完毕以后进程切换回用户态。类似的API函数有大约250多个,涵盖文件操作、进程控制、网络操作等

以上是用户态进程主动执行系统调用切换到内核态的方式,在程序出现未预知的异常和外围设备发出中断信息号时也会产生切换,后2种是被动切换 

 

二、基础概念

BIO:java.io包基于流模型实现,提供File抽象、输入输出流等IO的功能,交互方式是同步、阻塞的方式,在读写动作完成前线程会一直阻塞,java.net包下提供的部分网络API如Socket、ServerSocket也属于同步阻塞的IO类库,因为网络通信同样是IO行为

NIO:Java1.4中引入了NIO框架(java.nio 包),提供了Channel、Selector、Buffer等新的抽象,可以构建多路复用IO程序,同时提供更接近操作系统底层的高性能数据操作方式

AIO:Java7中,NIO有了进一步的改进,也就是NIO2,引入了异步非阻塞IO方式,也被称为AIO(Asynchronous IO),异步IO操作基于事件和回调机制

IO模型中同步异步与阻塞非阻塞的区别:应用获取数据的过程有2步,第一步是应用向内核请求数据,第二步是内核等待数据并将数据从内核空间复制到用户空间,同步异步是针对应用和内核之间的交互,阻塞非阻塞是针对内核与数据之间的交互

同步:应用进程向内核请求数据后,触发内核IO操作,在IO完成前一直等待(内核阻塞的情况,阻塞IO、同时轮询或监听多个IO请求的多路复用IO,因为进程调用selector是阻塞的)或者轮询(内核非阻塞的情况,非阻塞IO)查看,轮询虽然看上去没有结果就返回,像异步,但是最后一次来等待数据复制完成时还是需要等待的

异步:应用进程向内核请求数据后,就去做其他事情了,当IO操作完成后会得到通知(使用异步IO时,java将IO读写委托给内核,需要将数据缓冲区地址和大小提前传给内核,异步IO

阻塞:内核必须等待数据就绪后才能执行复制操作,应用进程一直等着

非阻塞:内核发现数据没好立刻返回,等到应用进程下次轮询进来如果数据就绪后再执行复制操作,或者内核发现没好时立即返回并让应用程序不要来了,等好了发信号通知再来(信号驱动IO,免去了轮询改为主动通知,但是复制数据时还是需要等待,也属于同步IO)

 

三、IO模型

1、blocking IO(阻塞IO)

 进程使用recvfrom()这个系统调用发起一个IO操作并等待,只有当内核等待数据、数据复制完成后,进程才会继续运行

2、nonblocking IO(非阻塞IO)

 

进程发起IO操作以后可以去做其他事情,但是进程需要时不时的询问IO操作是否就绪,会消耗不必要的CPU资源

3、IO multiplexing(多路复用IO)

多路复用中一个进程或线程通过使用selector可以同时轮询(select、poll)或者监听(epoll、kqueue)多个IO请求的内核操作,只要任意内核IO数据就绪,就返回通知,再进行系统调用recvfrom()完成IO操作

不过进程使用selector时还是阻塞的

4、signal driven IO(信号驱动IO)

进程发起IO请求时,可以注册一个信号函数然后立即返回,内核等待数据就绪后通过信号通知进程来调用recvfrom()完成IO操作

5、asynchronous IO(异步IO)

 

应用发起aio_read()请求后立刻返回,之后等待数据、复制数据内核都会自己完成,应用不用再调用recvfrom(),内核完成后再通知应用

6、维度划分

参考

Linux 内核空间与用户空间

彻底搞懂Java的网络IO

 

posted @ 2020-02-11 16:31  syxsdhy  阅读(245)  评论(0编辑  收藏  举报