查看原文
其他

真正涨知识了!Linux五大网络IO模型详解

嵌入式ARM 2021-01-31


01

什么是IO?

IO模型中,先讨论下什么是IO?


IO在计算机中指的就是Input/Output(输入/输出)。Input/Output(输入/输出)的内容当然就是data(数据)了。


那么数据被Input到哪,Output到哪呢?


  • Input(输入)数据到内存中,Output(输出)数据到IO设备(磁盘、网络等需要与内存进行数据交互的设备)中;

  • IO设备与内存直接的数据传输通过IO接口,操作系统封装了IO接口,我们编程时可以直接使用


02

阻塞/非阻塞与同步/异步


阻塞/非阻塞

针对的对象是调用者自己本身的情况


☞ 阻塞


指调用者在调用某一个函数后,一直在等待该函数的返回值,线程处于挂起状态。


☞ 非阻塞


指调用者在调用某一个函数后,不等待该函数的返回值,线程继续运行其他程序(执行其他操作或者一直遍历该函数是否返回了值)


同步/异步

针对的对象是被调用者的情况


☞ 同步


指的是被调用者在被调用后,操作完函数所包含的所有动作后,再返回返回值


☞ 异步


指的是被调用者在被调用后,先返回返回值,然后再进行函数所包含的其他动作。


03

五种IO模型


recvfrom/recv都是操作系统的内核函数,用于从(已连接)socket上接收数据,并捕获数据发送源的地址。


recv函数原型:ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags)


sockfd:接收端套接字描述符


buff:用来存放recv函数接收到的数据的缓冲区


nbytes:指明buff的长度


flags:一般置为0


对于一个应用程序即一个操作系统进程来说,它既有内核空间(与其他进程共享),也有用户空间(进程私有),它们都是处于虚拟地址空间中。用户进程是无法访问内核空间的,它只能访问用户空间,通过用户空间去内核空间复制数据,然后进行处理。


阻塞io(同步io):


发起请求就一直等待,直到数据返回。好比你去商场试衣间,里面有人,那你就一直在门外等着。(全程阻塞)



BIO程序流


应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。


当调用recv()函数时,系统首先查是否有准备好的数据。如果数据没有准备好,那么系统就处于等待状态。当数据准备好后,将数据从系统缓冲区复制到用户空间,然后该函数返回。在套接应用程序中,当调用recv()函数时,未必用户空间就已经存在数据,那么此时recv()函数就会处于等待状态。


非阻塞io(同步io):


不管有没有数据都返回,没有就隔一段时间再来请求,如此循环。好比你要喝水,水还没烧开,你就隔段时间去看一下饮水机,直到水烧开为止。(复制数据时阻塞)



非阻塞IO程序流


非阻塞IO通过进程反复调用IO函数(多次系统调用,并马上返回);在数据拷贝的过程中,进程是阻塞的;

我们把一个SOCKET接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间。


io复用(同步io):


I/O是指网络I/O,多路指多个TCP连接(即socket或者channel),复用指复用一个或几个线程。意思说一个或一组线程处理多个连接。比如课堂上学生做完了作业就举手,老师就下去检查作业。(对一个IO端口,两次调用,两次返回,比阻塞IO并没有什么优越性;关键是能实现同时对多个IO端口进行监听,可以同时对多个读/写操作的IO函数进行轮询检测,直到有数据可读或可写时,才真正调用IO操作函数。) 



IO多路复用程序流

select和epoll;对一个socket,两次调用,两次返回,比阻塞IO并没有什么优越性;

关键是能实现同时对多个socket进行处理。

I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。

当用户进程调用了select,那么整个进程会被block;而同时,kernel会“监视”所有select负责的socket;当任何一个socket中的数据准备好了,select就会返回。这个时候,用户进程再调用read操作,将数据从kernel拷贝到用户进程。

这个图和blocking IO的图其实并没有太大的不同,事实上还更差一些。因为这里需要使用两个系统调用(select和recvfrom),而blocking IO只调用了一个系统调用(recvfrom)。但是,用select的优势在于它可以同时处理多个connection。

(select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)

信号驱动io(同步io):


事先发出一个请求,当有数据后会返回一个标识回调,这时你可以去请求数据。好比银行排号,当叫到你的时候,你就可以去处理业务了(复制数据时阻塞)。



信号驱动IO程序流


套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。
当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。


两次调用,两次返回;


首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。


异步io:


发出请求就返回,剩下的事情会异步自动完成,不需要做任何处理。好比有事秘书干,自己啥也不用管。  



当一个异步过程调用发出后,调用者不能立刻得到结果。


实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作。


总结:




五种IO的模型:阻塞IO、非阻塞IO、多路复用IO、信号驱动IO和异步IO;前四种都是同步IO,在内核数据copy到用户空间时都是阻塞的。

阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果会那就是传统的阻塞IO,如果不会那就是非阻塞IO。

同步IO和异步IO的区别就在于第二个步骤是否阻塞,如果实际的IO读写阻塞请求进程,那么就是同步IO;如果不阻塞,而是操作系统帮你做完IO操作再将结果返回给你,那么就是异步IO。


-END-




推荐阅读



【01】Linux与windows相比究竟好在哪里?【02】最全盘点,哪款 Linux 桌面系统是你的菜?
【03】2020,Linux会有哪些新发展?【04】Unix 和 Linux 你不知道的那些历史【05】学会Linux正则表达式,我只用了3分钟


免责声明:整理文章为传播相关技术,版权归原作者所有,如有侵权,请联系删除

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存