当前位置: 首页 > news >正文

微网站 域名网站建设合作加盟

微网站 域名,网站建设合作加盟,wordpress 301 插件,一图读懂制作网站1、什么是I/O 在计算机操作系统中#xff0c;所谓的I/O就是输入#xff08;Input#xff09;和输出#xff08;Output#xff09;#xff0c;也可以理解为读#xff08;Read#xff09;和写#xff08;Write)#xff0c;针对不同的对象#xff0c;I/O模式可以划分为…1、什么是I/O 在计算机操作系统中所谓的I/O就是输入Input和输出Output也可以理解为读Read和写Write)针对不同的对象I/O模式可以划分为磁盘IO模型和网络IO模型。 IO操作会涉及到用户空间和内核空间的转换先来理解以下规则 内存空间分为用户空间和内核空间也称为用户缓冲区和内核缓冲区 用户的应用程序不能直接操作内核空间需要将数据从内核空间拷贝到用户空间才能使用 无论是read操作还是write操作都只能在内核空间里执行 磁盘IO和网络IO请求加载到内存的数据都是先放在内核空间的再来看看所谓的读Read和写Write)操作 读操作操作系统检查内核缓冲区有没有需要的数据如果内核缓冲区已经有需要的数据了那么就直接把内核空间的数据copy到用户空间供用户的应用程序使用。如果内核缓冲区没有需要的数据对于磁盘IO直接从磁盘中读取到内核缓冲区这个过程可以不需要cpu参与。而对于网络IO应用程序需要等待客户端发送数据如果客户端还没有发送数据对应的应用程序将会被阻塞直到客户端发送了数据该应用程序才会被唤醒从Socket协议找中读取客户端发送的数据到内核空间然后把内核空间的数据copy到用户空间供应用程序使用。 写操作用户的应用程序将数据从用户空间copy到内核空间的缓冲区中这时对用户程序来说写操作就已经完成至于什么时候再写到磁盘或通过网络发送出去由操作系统决定。除非应用程序显示地调用了sync命令立即把数据写入磁盘或执行flush()方法通过网络把数据发送出去。     绝大多数磁盘IO和网络IO的读写操作都是上述过程除了后面要讲到的零拷贝IO。 2、 网络IO 网络IO的流程如下 2.1、读操作 网络IO的既可以从物理磁盘中读数据也可以从socket中读数据从网卡中获取。当从物理磁盘中读数据的时候其流程和磁盘IO的读操作一样。当从socket中读数据应用程序需要等待客户端发送数据如果客户端还没有发送数据对应的应用程序将会被阻塞直到客户端发送了数据该应用程序才会被唤醒从Socket协议找中读取客户端发送的数据到内核空间这个过程也由DMA控制然后把内核空间的数据copy到用户空间供应用程序使用。 2.2、写操作 为了简化描述我们假设网络IO的数据从磁盘中获取读写操作的流程如下 当应用程序调用read()方法时通过DMA方式将数据从磁盘拷贝到内核缓冲区由cpu控制将内核缓冲区的数据拷贝到用户空间的缓冲区中供应用程序使用     当应用程序调用write()方法时cpu会把用户缓冲区中的数据copy到内核缓冲区的Socket Buffer中     最后通过DMA方式将内核空间中的Socket Buffer拷贝到Socket协议栈即网卡设备中传输。 网络IO的写操作也有四次缓冲区的copy第一次是从磁盘缓冲区到内核缓冲区由cpu控制第二次是内核缓冲区到用户缓冲区DMA控制第三次是用户缓冲区到内核缓冲区的Socket Buffer由cpu控制第四次是从内核缓冲区的Socket Buffer到网卡设备由DMA控制。四次缓冲区的copy工作两次由cpu控制两次由DMA控制。 2.3、 网络IO的延时 网络IO主要延时是由服务器响应延时带宽限制网络延时跳转路由延时本地接收延时 决定。一般为几十到几千毫秒受环境影响较大。所以一般来说网络IO延时要大于磁盘IO延时。 2.4. IO中断与DMA 以前传统的IO读写是通过中断由cpu控制的为了减少CPU对I/O的干预引入了直接存储器访问方式DMA方式。在DMA方式下数据的传送是在DMA的控制下完成的不需要cpu干预所以CPU和I/O设备可以并行工作提高了效率。现在来看看它们各自的原理 IO中断原理 用户进程通过read等系统调用接口向操作系统即CPU发出IO请求请求读取数据到自己的用户内存缓冲区中然后该进程进入阻塞状态。     操作系统收到用户进程的请求后进一步将IO请求发送给DMA然后CPU就可以去干别的事了。     DMA将IO请求转发给磁盘。     磁盘驱动器收到内核的IO请求后把数据读取到自己的缓冲区中当磁盘的缓冲区被读满后向DMA发起中断信号告知自己缓冲区已满。     DMA收到磁盘驱动器的信号将磁盘缓存中的数据copy到内核缓冲区中此时不占用CPUIO中断这里是占用CPU的。     如果内核缓冲区的数据少于用户申请读的数据则重复步骤3、4、5直到内核缓冲区的数据符合用户的要求为止。     内核缓冲区的数据已经符合用户的要求DMA停止向磁盘IO请求。     DMA发送中断信号给CPU。     CPU收到DMA的信号知道数据已经准备好于是将数据从内核空间copy到用户空间系统调用返回。     用户进程读取到数据后继续执行原来的任务。跟IO中断模式相比DMA模式下DMA就是CPU的一个代理它负责了一部分的拷贝工作从而减轻了CPU的负担。 需要注意的是DMA承担的工作是从磁盘的缓冲区到内核缓冲区或网卡设备到内核的soket buffer的拷贝工作以及内核缓冲区到磁盘缓冲区或内核的soket buffer到网卡设备的拷贝工作而内核缓冲区到用户缓冲区之间的拷贝工作仍然由CPU负责。 2.5、零拷贝IO   在上述IO中读写操作要经过四次缓冲区的拷贝并经历了四次内核态和用户态的切换。 零拷贝zero copyIO技术减少不必要的内核缓冲区跟用户缓冲区之间的拷贝从而减少CPU的开销和状态切换带来的开销达到性能的提升。 在zero copy下如果从磁盘中读取文件然后通过网络发送出去只需要拷贝三次只发生两次内核态和用户态的切换。 下图是不使用zero copy的网络IO传输过程 零拷贝的传输过程硬盘 kernel buffer (快速拷贝到kernel socket buffer) Socket协议栈网卡设备中 当应用程序调用read()方法时通过DMA方式将数据从磁盘拷贝到内核缓冲区 由cpu控制将内核缓冲区的数据直接拷贝到另外一个与 socket相关的内核缓冲区即kernel socket buffer然后由DMA 把数据从kernel socket buffer直接拷贝给Socket协议栈网卡设备中。 这里只经历了三次缓冲区的拷贝第一次是从磁盘缓冲区到内核缓冲区第二次是从内核缓冲区到kernel socket buffer第三次是从kernel socket buffe到Socket协议栈网卡设备中。只发生两次内核态和用户态的切换第一次是当应用程序调用read()方法时用户态切换到内核到执行read系统调用第二次是将数据从网络中发送出去后系统调用返回从内核态切换到用户态。零拷贝zero copy的应用  Linux下提供了zero copy的接口sendfile和splice用户可通过这两个接口实现零拷贝传输 Nginx可以通过sendfile配置开启零拷贝 在linux系统中Java NIO中FileChannel.transferTo的实现依赖于 sendfile()调用。 Apache使用了sendfile64()来传送文件sendfile64()是sendfile()的扩展实现  kafka也用到了零拷贝的功能具体我没有深究 注意零拷贝要求输入的fd必须是文件句柄不能是socket输出的fd必须是socket也就是说数据的来源必须是从本地的磁盘而不能是从网络中如果数据来源于socket就不能使用零拷贝功能了。我们看一下sendfile接口就知道了 #include sys/sendfile.h ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count)out_fd待写入文件描述符in_fd 待读出文件描述符offset从读入文件流的哪个位置开始读如果为空则默认从起始位置开始count指定在文件描述符in_fd 和out_fd之间传输的字节数返回值成功时返回出传输的字节数失败返回-1 in_fd必须指向真实的文件不能是socket和管道而out_fd则必须是一个socket。由此可见sendfile几乎是专门为在网络上传输文件而设计的。 在Linxu系统中一切皆文件因此socket也是一个文件也有文件句柄或文件描述符。 3、 BIO import java.io.IOException; import java.net.ServerSocket; import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {ServerSocket serverSocket new ServerSocket();serverSocket.bind(new InetSocketAddress(9999)); // 绑定端口号9999while (true) {Socket client serverSocket.accept(); // 阻塞等待客户端连接new Thread(new Accept(client)).start(); // 为每个客户端连接创建一个新线程处理请求}} }class Accept implements Runnable {private Socket client null;public Accept(Socket client) {this.client client;}Overridepublic void run() {try (InputStream inputStream client.getInputStream();BufferedReader reader new BufferedReader(new InputStreamReader(inputStream))) {String dataLine;while ((dataLine reader.readLine()) ! null) {System.out.println(dataLine); // 处理接收到的数据}} catch (IOException e) {e.printStackTrace();} finally {try {client.close(); // 关闭客户端连接} catch (IOException e) {e.printStackTrace();}}} }现在我们就来讲解BIO、NIO、IO多路复用、AIO在这之前我必须强调这些IO大多用于网络IO并且这里主要介绍用户程序从网络中获取数据那一部分。一方面是为了方便描述另一方式更能体现出这些IO的区别。 网络IO从Socket获取数据的步骤 1用户进程执行系统调用转入内核态 2操作系统等待远处客户端发送数据前提是客户端和服务器通过TCP三次握手成功客户端发送数据后操作系统通过从网卡设备获取数据并把数据从Socket协议栈拷贝到内核缓冲区 3把内核缓冲区的数据拷贝到用户缓冲区 4用户进程获取到数据继续执行BIO、NIO、AIO的主要区别在于 步骤1里用户进程执行系统调用后的状态如何是阻塞或挂起还是非阻塞。步骤3里把内核缓冲区的数据拷贝到用户缓冲区在拷贝过程中用户进程的状态又如何是阻塞还是非阻塞。如果用户进程在步骤1执行后的状态是阻塞的且步骤3过程中进程也是阻塞的那么是BIO同步阻塞IO。如果用户进程在步骤1执行后的状态是非阻塞的且步骤3过程中进程是阻塞的那么是NIO同步非阻塞IO。 如果用户进程在步骤1执行后的状态是非阻塞的且步骤3过程中进程也是非阻塞的也就是说真正读或写时进程的状态是非阻塞的那么是AIO异步IO。至于多路复用IO和BIO、NIO、AIO的区别后面会细细讲解。那么我们就开始吧 BIO Blocking I/O称之为同步阻塞I/O其IO模型传输如下图所示 上图红色表示进程处理阻塞状态绿色表示进程处于非阻塞状态 我相信BIO模型的传输过程上图已经描述很清楚了可以看到BIO模型的用户进程在执行系统调用后一直处于阻塞状态等待内核数据到位后进程继续阻塞直到内核数据拷贝到用户空间。 该模式下一个线程只能处理一个Socket IO连接高并发时服务端会启用大量的线程来处理多个Socket由于是阻塞IO会导致大量线程处于阻塞状态导致cpu资源浪费且大量线程会导致大量的上下文切换造成过多的开销。     当前绝大操作系统都支持多线程当操作系统引入多线程之后进程的执行实际就是进程中的多个线程在执行同一时刻cpu只能执行一个线程多个线程通过轮询的方式交替执行。 这时你可能会有疑问用户进程都被阻塞或挂起了在内核态还怎么操作呢事实上read和write都是内核级的操作只要用户进程调用相应的系统调用接口后内核进程或线程在真正执行读和写操作硬件时与用户进程就没什么关系了。 4. NIO NIO Non-blocking IO称之为非阻塞IO其传输过程如下 在NIO模式下当用户进程执行系统调用后如果当前数据还没有准备好则会立即返回NIO的非阻塞就提现在这里然后再次进行系统调用不断测试数据是否准备好。如果数据准备好了当前进程会进入阻塞转态直到数据从内核空间拷贝到用户空间进程才会被唤醒就可以处理数据了。 NIO模式下一个线程就可以处理多个Socket连接没必要开启多线程进行处理如果多个NIO会有多个线程一起执行多次系统调用结果会很可怕。但是当有1000个Socket连接时用户进程会以轮询的方式执行1000次系统调用判断数据有没有准备好即会发生1000次用户态到内核态的切换成本几何上升。即使当前只有一个Socket连接也会重复进行系统调用因为此时的用户进程不仅要接收新的Socket连接并把它拷贝到内核还要判断已有的Socket连接是否准备好数据这都会有系统调用极大的浪费cpu资源。 5、 IO多路复用 IO多路复用的传输过程如下 import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set;public class NIOServer {public static void main(String[] args) throws IOException {// 打开SelectorSelector selector Selector.open();// 打开ServerSocketChannelServerSocketChannel serverSocket ServerSocketChannel.open();serverSocket.configureBlocking(false);serverSocket.socket().bind(new InetSocketAddress(8000));// 注册ServerSocketChannel到SelectorserverSocket.register(selector, SelectionKey.OP_ACCEPT);// 轮询就绪的通道while (true) {// 非阻塞地选择就绪的通道selector.select();// 获取就绪的SelectionKey集合SetSelectionKey selectedKeys selector.selectedKeys();IteratorSelectionKey it selectedKeys.iterator();// 迭代就绪的通道while (it.hasNext()) {SelectionKey key it.next();it.remove();// 处理就绪的通道if (key.isAcceptable()) {ServerSocketChannel ssc (ServerSocketChannel) key.channel();SocketChannel socketChannel ssc.accept();socketChannel.configureBlocking(false);// 注册新接入的通道到SelectorsocketChannel.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) {SocketChannel socketChannel (SocketChannel) key.channel();int count;StringBuilder buffer new StringBuilder();// 读取数据while ((count socketChannel.read(buffer)) 0) {// 处理读取到的数据}}// 其他事件类型如OP_WRITE等可以在此处进行处理}}} } 由于NIO会多次执行系统调用进行测试大大浪费系统的资源而多路复用IO把轮询多个Socket文件句柄的事情放在内核空间里执行即让内核负责轮询所有socket这样就不会有用户态和系统态的切换当某个或几个socket有数据到达了返回所有就绪的Socket文件句柄给用户进程然后用户进程执行read系统调用接口并进入阻塞状态。内核进程或线程把数据从内核空间拷贝到用户空间用户进程读取到数据就可以进行处理了。 多路复用IO在执行系统调用后进程就处于阻塞状态所以多路复用IO本质上也是同步阻塞IO只不过它是在内核态轮询所有socket大大提高了IO的处理速度也减少了系统状态切换的开销。此外它与同步阻塞的BIO不同多路复用IO可以使用一个线程同时处理多个Socket的IO请求这是BIO做不到的。而在BIO中必须通过多线程的方式才能达到这个目的。 另外大家可以思考一下为什么用户进程从网络中获取数据的第一步就要执行系统调用我举一个例子来说明。 假如一个服务端上的用户进程要读取客户端发来的数据此时用户进程在用户态当进程执行了accept()方法获取客户端的链接此时就得到了客户端Socket的文件句柄或文件描述符但是该用户进程并不知道该Socket的文件句柄是否就绪即是否可读这就要执行系统调用进入内核态并把当前网络连接的Socket文件句柄或文件描述符复制到内核态。为什么要进入内核态呢因为数据是从Socket协议栈或网卡设备发过来的要操作硬件设备才能读取数据所以必须在内核态下判断客户端的Socket是否发来消息。进入内核态以后内核进程会判断该Socket是否可读即是否准备好数据如果准备好了数据就把数据从Socket协议栈或网卡设备拷贝到内核缓冲区再把内核缓冲区的数据拷贝到用户缓冲区。所以只要有一个客户端的Socket连接到来就会进入一次系统调用判断Socket的文件句柄是否就绪。这里可能不好理解但对下面多路复用模式的理解很有用处。                          多路复用模式包含三种即select、poll和epoll这几种模式主要区别在于获取可读Socket文件句柄的方式。 6、 select select方法本质其实就是维护了一个文件描述符fd数组以此为基础实现IO多路复用的功能。这个fd数组有长度限制在32位系统中最大值为1024个而在64位系统中最 大值为2048个这个配置可以调用。 select方法被调用首先需要将fd_set从用户空间拷贝到内核空间然后内核用poll机制此poll机制非IO多路复用的那个poll方法直到有一个fd活跃或者超时了方法返回。 fd_set在用户空间和内核空间的频繁复制效率低。单个进程可监控的fd数量有限制无论是1024还是2048对于很多情景来说都是不够用的。基于轮询来实现效率低。 7 poll poll本质上和select没有区别依然需要进行数据结构的复制依然是基于轮询来实现但区别就是select使用的是fd数组而poll则是维护了一个链表所以从理论上poll方法中单个进程能监听的fd不再有数量限制。但是轮询复制等select存在的问题poll依然存在。 8 epoll epoll就是对select和poll的改进了。它的核心思想是基于事件驱动来实现的实现起来也并不难就是给每个fd注册一个回调函数当fd对应的设备发生IO事件时就会调用这个回调函数将该fd放到一个链表中然后由客户端从该链表中取出一个个fd以此达到O1的时间复度。 poll操作实际上对应着有三个函数epoll_createepoll_ctrepoll_wait epoll_create 相当于在内核中创建一个存放fd的数据结构。在select和poll方法中内核都没有为fd准备存放其的数据结构只是简单粗暴地把数组或者链表复制进来而epoll则不一样epoll_create会在内核建立一颗专门用来存放fd结点的红黑树后续如果有新增的fd结点都会注册到这个epoll红黑树上。 epoll_ctr 另一点不一样的是select和poll会一次性将监听的所有fd都复制到内核中而epoll不一样当需要添加一个新的fd时会调用epoll_ctr给这个fd注册一个回调函数然后将该fd结点注册到内核中的红黑树中。当该fd对应的设备活跃时会调用该fd上的回调函数将该结点存放在一个就绪链表中。这也解决了在内核空间和用户空间之间进行来回复制的问题。 epoll_wait epoll_wait的做法也很简单其实直接就是从就绪链表中取结点这也解决了轮询的问题时间复杂度变成O(1)所以综合来说epoll的优点有 没有最大并发连接的限制远远比1024或者2048要大。江湖传言1G的内存上能监听10W个端口。 效率变高。epoll是基于事件驱动实现的不会随着fd数量上升而效率下降。 减少内存拷贝的次数。 9. AIO AIO Asynchronous I/O异步非阻塞I/O模型。传输过程如下 可以看到异步非阻塞I/O在判断数据有没有准备好即Socket是否就绪和真正读数据两个阶段都是非阻塞的。AIO在第一次执行系统调用后会注册一个回调函数内核在检测到某Socket文件句柄就绪调用该回调函数执行真正的读操作将数据从内核空间拷贝到用户空间然后返回给用户使用。在整个过程用户进程都是非阻塞状态可以做其它的事情。没有Linux系统采用AIO模型只有windows的IOCP是此模型。 10、 总结 IO可以分为两个阶段第一阶段判断有没有事件发生或判断数据有没有准备好或判断Socket是否就绪第二阶段在数据准备好以后执行真正的读或写操作将数据从内核空间拷贝到用户空间。这几个阶段     同步阻塞IOBIO两个阶段的用户进程都阻塞。     同步非阻塞IONIO第一阶段没有阻塞但是用户进程或线程必须不断的轮询判断有没有Socket就绪这时cpu疯狂被占用。第二阶段数据拷贝的过程是阻塞的。所以所有的同步过程在第二阶段都是阻塞的尽管这是非阻塞的调用。     多路复用NIO的第一阶段没有阻塞但是由用户线程不断轮询多个Socket有没有就绪。而多路复用把这件事情交给一个内核线程去处理速度非常快。select和poll机制下第一阶段是也是阻塞的而epoll机制用户线程除了要执行epoll_create还要执行epoll_ctl和epoll_wait所以是非阻塞的。在第二阶段所有的多路复用IO都是阻塞的。所以多路复用IO也是同步IO。     异步IOAIO两个阶段都是非阻塞的。另外不得不提的是上述的阻塞和非阻塞指的是IO模型用户进程获取数据后执行业务逻辑的时候也分异步和同步。比如进程执行一段很复杂的业务逻辑需要很长的时间才能返回也可以注册一个回调函数等待此段代码执行完毕后就通知用户进程。例如nginx在Linux2.6以后的内核中用的IO模型是epoll即同步IO而Nginx的worker进程的处理请求的时候是异步的。业务逻辑的同步和异步概念如下     同步所谓同步就是在发出一个功能调用时在没有得到结果之前该调用就不返回。     异步异步的概念和同步相对。当一个异步过程调用发出后调用者不能立刻得到结果。实际处理这个调用的部件在完成后通过状态、通知和回调来通知调用者。     阻塞阻塞调用是指调用结果返回之前当前线程会被挂起函数只有在得到结果之后才会返回。有人也许会把阻塞调用和同步调用等同起来实际上它们是不同的。对于同步调用来说很多时候当前线程还是激活的只是当前函数没有返回而已。     非阻塞非阻塞和阻塞的概念相对应指在不能立刻得到结果之前该函数不会阻塞当前线程而会立刻返回。
http://www.dnsts.com.cn/news/88101.html

相关文章:

  • 做全英文网站元气森林网络营销案例
  • 西宁网站建设报价ew君博贴心品牌宣传网站制作
  • 高端网站设计公司上海建设工程新工艺网站
  • js网站模板湖南seo排名
  • 做网站干什么用wordpress 4.9.6 漏洞
  • 做读书网站的前景企业微信登录网页版
  • 富顺做网站wordpress 改变字体
  • 网站的设计要素wordpress 定时发送
  • 做会所在哪个网站推广公司网站首页的图片怎么做
  • 做网站必须要虚拟主机吗清新网站模板
  • wap电影网站建设成都网站设计建设
  • 做公司的网站有哪些东西吗宁夏做网站的公司
  • 男生和女生做污的事情免费网站邯郸市第一医院
  • 家政网站制作南京做企业网站公司哪家好
  • 广东佛山建网站柳州企业网站建设公司
  • 做网站自己买服务器吗湖州长兴建设局网站
  • 网站建设平台一般多少钱枣庄网站开发公司
  • 湛江专业建站推荐网销工作内容简述
  • 私人网站制作劳务 东莞网站建设
  • 外贸网站建站那家公司好wordpress用户中心模板
  • 成都定制网站建设服网站建设的一般流程是
  • 温州编程网站济南房产网新开楼盘
  • 哪个公司网站做的最好销售管理软件哪个好用
  • 内江网站开发长沙注册公司核名网站
  • 海洋公司做网站专业手机网站公司吗
  • 珠海建站平台双创网站建设
  • 设计网页多少钱一个页面seo培训机构
  • 广东快速做网站公司网站模版如何使用
  • 四川省城市建设培训中心 网站哪个网站做高中的题好
  • 商城网站作品有口碑的大连网站建设