同步 IO 与异步 IO
同步 IO 和异步 IO 是关于数据读写方式的两种不同模式。
同步 IO 是指在程序读写数据时,需要等待操作完成后才能继续执行后面的程序。这种模式下,当程序使用阻塞式 IO 时,会一直等待IO操作完成,程序会暂停执行,直至IO操作完成,这样会导致程序的执行效率降低。同步 IO 主要用于小型程序,如批处理作业,简单计算和查询程序等。
同步IO的特点:
1.同步IO指的是用户进程触发I/O操作并等待或者轮询的去查看I/O操作是否就绪。
2.同步IO的执行者是IO操作的发起者。
3.同步IO需要发起者进行内核态到用户态的数据拷贝过程,所以这里必须阻塞。
异步 IO 是指在进行数据读写操作时,程序无需等待 IO 操作完成。异步 IO 使得程序可以在 IO 操作的同时执行其他操作,提高了程序的执行效率。异步 IO 主要用于高并发场景,如 Web 服务器、数据库访问等。
异步IO的特点:
1.异步IO是指用户进程触发I/O操作以后就立即返回,继续开始做自己的事情,而当I/O操作已经完成的时候会得到I/O完成的通知。
2.异步IO的执行者是内核线程,内核线程将数据从内核态拷贝到用户态,所以这里没有阻塞。
五种网络IO模式
对于一次IO访问(以read为例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,会经历两个阶段:
1、等待数据准备
2、将数据从内核拷贝到进程中
linux系统产生了下面五种网络模式的方案:
1、阻塞IO(blocking IO)
2、非阻塞IO(nonblocking IO)
3、IO多路复用(IO multiplexing)
4、信号驱动IO(signal driven IO)不常用
5、异步IO (asynchronous IO)
阻塞 IO
阻塞:当某个事件或者任务在执行过程中,它发出一个请求操作,但是由于该请求操作需要的条件不满足,那么就会一直在那等待,直至条件满足;
同步和异步着重点在于多个任务的执行过程中,一个任务的执行是否会导致整个流程的暂时等待;
而阻塞和非阻塞着重点在于发出一个请求操作时,如果进行操作的条件不满足是否会返会一个标志信息告知条件不满足。
在linux 中,默认情况下所有的 socket 都是 blocking IO, 一个典型的读操作流程:
非阻塞 IO
非阻塞:当某个事件或者任务在执行过程中,它发出一个请求操作,如果该请求操作需要的条件不满足,会立即返回一个标志信息告知条件不满足,不会一直在那等待。
阻塞和非阻塞的区别关键在于当发出请求一个操作时,如果条件不满足,是会一直等待还是返回一个标志信息。
当用户线程发起一个 read 操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送 read 操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。
所以事实上,在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。
设置非阻塞常用方式:
方式一: 创建socket 时指定
int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
方式二: 在使用前通过如下方式设定
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
IO 多路复用
在多路复用 IO 模型中,会有一个线程不断去轮询多个 socket 的状态,只有当 socket 真正有读写事件时,才真正调用实际的 IO 读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个 socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket读写事件进行时,才会使用 IO 资源,所以它大大减少了资源占用。
多路复用IO为何比非阻塞IO模型的效率高 是因为在非阻塞 IO 中,不断地询问 socket 状态是通过用户线程去进行的,而在多路复用 IO 中,轮询每个 socket 状态是内核在进行的,这个效率要比用户线程要高的多。
不过要注意的是,多路复用IO模型是通过轮询的方式来检测是否有事件到达,并且对到达的事件逐一进行响应。因此对于多路复用IO模型来说,一旦事件响应体很大,那么就会导致后续的事件迟迟得不到处理,并且会影响新的事件轮询。
信号驱动 IO
在信号驱动 IO 模型中,当用户线程发起一个 IO 请求操作,会给对应的 socket 注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用 IO 读写操作来进行实际的 IO 请求操作。
使用信号驱动 I/O 时,当网络套接字可读后,内核通过发送 SIGIO 信号通知应用进程,于是应用可以开 始读取数据。该方式并不是异步 I/O,因为实际读取数据到应用进程缓存的工作仍然是由应用自己负责的。
异步 IO
在异步IO模型中,当用户线程发起 read 操作之后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它收到一个 asynchronous read 之后,它会立刻返回,说明 read 请求已经成功发起了,因此不会对用户线程产生任何 block。然后,内核会等待数据准备完成,然后将数据拷贝到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它 read 操作完成了。
也就说用户线程完全不需要知道实际的整个IO操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示IO操作已经完成,可以直接去使用数据了。
也就说在异步IO模型中,IO操作的两个阶段都不会阻塞用户线程,这两个阶段都是由内核自动完成,然后发送一个信号告知用户线程操作已完成。
前面四种IO模型实际上都属于同步IO,只有最后一种是真正的异步IO,因为无论是多路复用IO还是信号驱动模型,IO操作的第2个阶段都会引起用户线程阻塞,也就是内核进行数据拷贝的过程都会让用户线程阻塞。
https://www.cnblogs.com/xiaoxi/p/6525396.html