什么是I/O多路复用模型?

本文解释了文件在计算机中的基本概念,阐述了“一切皆文件”的理念,介绍了open(),read(),write()等IO操作,重点讲解了文件描述符和I/O多路复用在处理大量并发连接时的高效策略,以Linux的epoll为例说明其实现原理。
摘要由CSDN通过智能技术生成

文件

什么是文件?

文件、计算机文件(英语:computer file)是存储在某种长期储存设备或临时存储设备中的一段数据流,并且归属于计算机文件系统管理之下。

我们可以简单来理解,一个文件在计算机底层就是一个 n 比特的序列。
在这里插入图片描述

如何理解 Everything is file ?

作为程序员,肯定听过这样一句话:一切皆文件。磁盘、网络、终端,甚至用于进程之间通信的工具PiPe等,都可以被当作文件来对待。
在这里插入图片描述

为什么要设计Everything is file ?

因为既然磁盘、网络、终端、pipe等都是文件,那么我们就可以用一套接口来操作一切
在这里插入图片描述

我们可以用一套接口,来实现对所有设备的读写。常用的IO操作一般有这样几类:

open()
read()
write()
close()

程序通过这几个接口,几乎可以完成大部分IO操作。这就是文件这个概念的强大之处,这也是为什么科学家们要设计Everything is file。

open函数的参数和返回值是什么?

open函数的参数显然应该是文件路径,这很好理解。

open("file path")

open应该返回什么呢?open应该返回一些东西,好让我们来调用read,wirte函数。

?=open("file path")
read(?)
write(?)

我们在读写文件时,操作系统内部,也就是内核中,会创建各种各样的数据结构。内核利用这些数据结构就能知道该读写哪个文件,这个文件的数据保存在了磁盘上什么位置,这个文件读写到了那里等信息。

open可以简单的返回一个数字,这个数字告诉内核,那么内核就可以找到数据结构,进而真正地操作文件。

在这里插入图片描述

这个数字就是实际上就是内核数据结构的一个索引,和数组的下标一样。
在这里插入图片描述
但是,把open返回这个数组叫做内核数据结构索引好像和文件没有什么关联,于是,人们又起了一个新的名字:文件描述符,这就是文件描述符的由来。

在这里插入图片描述

接下来我们只需要把这个文件描述符传给read和write就可以读写文件了。

int fd = open("file path")
read(fd, ...)
write(fd, ...)

fd就是File Description的缩写

理解了文件,文件描述符是怎么回事之后,就可以理解I/O多路复用了。

网络编程为例理解I/O多路

当浏览器向Web服务器发起tcp连接请求,经过三次握手,建立连接后。Web服务器通过调用accept方法,同样会得到一个文件描述符。
在这里插入图片描述

int conn_fd = accept(...);

通过读写该文件描述符,就可以与客户端(浏览器)进行通信。

服务器端的处理逻辑通常是读取客户端发送过来的请求,然后执行某些特定逻辑。

if(read(conn_fd,buff)>0){
	do_something(buff);
}

当只有一个文件描述符的时候,这个代码非常简单。这时候对应的情况是只有一个客户端和服务器进行连接。但是处于因特网中的服务器通常会与成千上万台,甚至更多台客户端进行连接,这时候就会产生大量的文件描述符。

假设有3个客户端与服务器进行了连接。服务器分别调用3次accept函数得到3个文件描述符,然后读取文件描述符执行特定逻辑。那服务器又该怎么处理呢?

最简单的方法是这样,进行串行化处理,一次读取这3个文件描述符,一个逻辑处理完了在去读取下一个文件描述符,处理对应的逻辑。

if(read(conn_fd1,buff)>0){
	do_something(buff);
}
if(read(conn_fd2,buff)>0){
	do_something(buff);
}
if(read(conn_fd3,buff)>0){
	do_something(buff);
}

但是,这样会有一个问题,就是read是一个典型的阻塞操作,如果第一个链接迟迟没有发送数据,那么read函数是不会返回,就会导致read(conn_fd1,buff)>0句代码迟迟不会执行,然后导致其它所有客户端的处理被卡住。
在这里插入图片描述

为什么串行化处理文件描述符不行?

因为当有一堆文件描述符需要处理时,下一个文件描述符的处理必须等到前面一个文件描述符处理完成。但是,由于不同客户端的实际情况不同,在某些时侯,前面的文件描述发对应的客户端迟迟没有发送网络数据,而read是一个阻塞操作,如果没读到数据,不会结束,而是等待。将出现阻塞在read这句代码,不完后执行,对于其它客户端来说,就好像服务器卡死了。

服务器更好地如何处理多个文件描述符?

服务器可以把获取到底文件描述符一股脑地扔给操作系统内核。假设服务器产生了1万个文件描述符,它把这些文件描述符扔给内核,并告诉内核,你替我监管着,有可以读写的文件描述符就告诉并返回给我,我去执行该文件描述符对应的逻辑。
在这里插入图片描述

这段过程对应的代码如下:

fds = wait_files({fd1,fd2,fd3,...,fdn});
for(fd:fds){
  read(fd, buf);
  do_something(buf);
}

当我们调用wait_files,给内核传一堆文件描述符后。这个wait_files函数就会阻塞,直到任何一个文件描述符代表的文件可读或者可写时,wait_files就会返回一个就绪的文件描述符列表。这时,对这些文件描述符背后的文件进行操作就不会阻塞了。

显然,这是一种更加高效,并且不太一样的I/O处理机制,需要有一个新的名字。为了更加逼格更高一点,人们把它叫做I/O多路复用。这就是I/O多路复用的由来。

I/O多路复用就是应用程序把一堆需要处理的文件描述符丢给内核,当其中任何一个文件描述符背后的文件具备读写条件时,就把这些文件描述符筛选出来给应用程序处理。

I/O多路复用机制,在不同的操作系统有不同的实现,比如Linux的epoll,但它们的核心原理都一样,都是类似与这样的代码:

fds = wait_files({fd1,fd2,fd3,...,fdn});
for(fd:fds){
  read(fd, buf);
  do_something(uf);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值