前言
在理解这些概念之前,我们要先知道:我们调用write这个函数时,其实只是将用户态的数据给到内核态中,然后内核操作系统会帮我们完成接下来的操作;同理,调用read函数,其实是将内核态中的数据拷贝到用户态里,而内核是怎样获取到这些数据的,跟底层的协议有关。
例如:网络编程中,当客户端先服务器发送数据时,服务端接收到数据后,是将数据放置在内核的缓冲区,然后我们程序中调用recv或者read,其实是读取的这个缓冲区里的数据。
所以,读取数据一般会分为两个过程:【内核数据是否准备好】 和 【数据从内核态拷贝到用户态】
阻塞和非阻塞
知道上面的知识后,就可以理解阻塞和非阻塞的区别了。
阻塞和非阻塞是指【内核数据是否准备好】这个过程,假设我们调用read阻塞去读取数据时,当内核数据还没准备好,我们程序就会一直停在那个地方,不会往下执行。直到当内核数据准备好时,read函数才会继续完成【数据从内核态拷贝到用户态】这个过程。最后再然后返回。这个过程就产生了阻塞等待内湖数据的到达。
而当我们调用read去读取,如果此时内核数据已经准备好了,我们的read函数就能立执行【数据从内核态拷贝到用户态】这个过程,然后返回,因此就不会产生阻塞。
所以阻塞和非阻塞这个概念指的是:内核数据是否准备好,也就是数据是否到达内核这个过程。当数据还没到达内核时,我们要一直去等他到达,就叫做阻塞。 而当数据未到达内核时,我们直接返回不等待数据了,就叫做非阻塞。
同步和异步
首先我们要知道,上述的阻塞和非阻塞的概念全都是属于同步!
然后同步和异步讨论的是【数据从内核态拷贝到用户态】这个过程。具体是指:当内核数据准备好时,我们需不需要主动地调用函数去读取这个数据。
对于同步:当内核数据准备好时,我们需要主动地调用read函数去从内核里读取这个数据,也就是需要等待数据从内核态拷贝到用户态这个过程。因此无论 read 和 send 是阻塞 I/O,还是非阻塞 I/O 都是同步调用。因为在 read 调用时,内核将数据从内核空间拷贝到用户空间的过程都是需要等待的,也就是说这个过程是同步的,如果内核实现的拷贝效率不高,read 调用就会在这个同步过程中等待比较长的时间。
对于异步:异步没有阻塞和非阻塞的概念,因为它【内核数据是否准备好】和【数据从内核态拷贝到用户态】这两个过程都不用等待。我们只需要一开始先内核注册好这个事件,此后当内核数据到来时,操作系统就会自动帮我们完成【数据从内核态拷贝到用户态】这个过程,我们不需要主动地调用read函数去等待这个过程! 然后当数据从内核态拷贝到用户态这个过程完成之后,操作系统就会以一个信号或者回调函数的方式通知用户 数据已经准备好了。
Reactor和Proactor网络模式
Reactor是非阻塞的同步网络模式,Proactor是异步网络模式。
Reactor:在此模式中,一般由主线程进行客户端文件描述符的监听,当有客户端连接时,服务器进行连接的建立。同时监听事件,当有客户端产生可读事件时,主线程会读取数据然后将这些数据给到线程池里的线程进行业务处理,最后再由主线程将数据发送回去给客户端。
Proactor:主线程只监听客户端的连接,数据的读取、发送和业务处理都在线程池的线程中进行,并且是通过异步的方式读取数据。