“曾经有一份真诚的爱情放在我面前,但我没有珍惜,等我失去的时候我才后悔莫及,人世间最痛苦的事莫过于此。”这是电影《大话西游》中的经典台词,大圣与紫霞仙子最终还是没有有情人成眷属。然而我们今天介绍的主角-I/O与用户,二者却是经历了各种考验,最后终成眷属了~
那么什么是IO呢?
说到IO就得说到操作系统Linux,在Linux的世界里,一切都是文件,都是一串二进制流,不管是socket、FIFO、管道、终端,全都是文件,全都是流。信息交换的过程就是对流进行数据的收发操作,也就是IO操作(Input/Output)。而计算机世界里有这么多的流,通过文件描述符就可以知道操作哪个流了,通常是创建一个socket套接字就会产生一个文件描述符,所以对文件描述符的操作也就是对socket的操作,往里写入数据调用write,读数据调用read。
在介绍I/O模型之前,我们要给大家先普及两个概念:内核空间和用户空间。简单来说,就是操作系统在设计时为了让各个模块各干各的事,划分了不同的地盘。现代操作系统采用虚拟存储器,有32位和64位操作系统,因此寻址地址(虚拟存储空间)包含4G(2的32次方)、17179869184G(2的64次方)。操作系统的核心是内核,对上可以访问受保护的内存空间,对下可以访问底层硬件设备。为了内核安全,保障用户进程不能直接操作内核,因此操作系统把虚拟空间划分为用户空间和内核空间。用户空间执行用户自己的代码时,处于用户态;通过系统调用进入内核执行时,处于内核态。
讲完了用户空间、内核空间、用户态、内核态,进入今天的主题I/O。故事的开始是用户在前端发起的请求,当用户在前端发起一个请求时,我们知道的路径是该请求通过网络传递给到后端,后端经过业务程序调用、数据库调用等将结果计算出来,再传递给到前端展示结果给用户。事实上,这背后还有更复杂的技术原理。从整个软件系统来看,除了业务程序之外、还包含操作系统、硬件资源,因此整个用户请求的处理必定是软硬件都在参与。整个协同的过程就像人饿了要吃饭一样,身体接收到饿了的讯息,经由大脑处理想吃肉,再通过眼睛看到食材、双腿获取食材、双手烹饪,经过嘴巴消化之后,反馈给大脑饱了的讯息。
整个web请求执行的过程如下:
用户在客户端发起请求到服务器网卡;
服务器网卡接收到请求后转给内核处理;
内核根据请求对应的套接字,将请求交给工作在用户空间的web服务器进程;
web服务器进程根据用户请求,向内核进行系统调用,申请相应资源;
当web服务器进程请求的是存放在本地磁盘上的资源,内核会通过驱动程序连接磁盘;
内核调用磁盘,获取相应的资源;
内核将资源存放在自己的缓存区并通知web服务器进程;
web服务器进程通过系统调用取得资源,并把它复制到进程自己的缓冲区中;
web服务器进程形成响应,通过系统调用再次发给内核响应请求;
内核将响应发给网卡;
网卡把响应结果传递给到用户。
在整个请求的执行过程中,有两次IO操作,第一次是客户端请求的网络I/O,第二次是web服务器请求磁盘I/O。因此当应用程序处理用户请求缓慢时,运维和研发也会关注网络IO和磁盘IO的情况;监控系统也会包含网络读写速度、网络发送接收包数量、磁盘读写速度等指标的监控。
讲完了为什么系统需要I/O,我们再来看看I/O有哪些模型?根据执行过程和通信过程的不同,IO模型分为阻塞型、非阻塞型、IO复用型、信号驱动型IO、异步IO。简单来说,就是这五个模型的不同在于等待数据准备阶段、将数据从内核拷贝到进程中这两个阶段不相同。
阻塞IO,即在IO操作执行时,所有其它相关的操作都不能进行,直到IO操作执行完成。当在内核执行IO操作时,应用程序调用IO的recvform函数,将应用程序调起,不给它分配CPU时间片,进入阻塞状态后直到IO操作结束才返回结果;如果系统内核数据没有准备好,那么就一直处理等待状态。整个过程就像你在餐馆点菜一样,确认好菜单后服务员会传递给到后厨,在整个期间内你只能待在餐桌,哪里都不能去,处于阻塞等待状态,直到菜做好了,上菜后才可以走动。
非阻塞IO,即在IO操作执行时,其它的相关操作也可以进行。用户线程向内核发起IO请求时,因为内核执行也需要时间,所以先返回给用户一个状态值“EWOULDBLOCK”,在操作执行过程期间,用户线程不断的轮询IO请求,直到读取到数据。整个过程就像你在餐馆点菜一样,点好菜后服务员会给你个订单和号码牌,你可以到处走动,也可以去问厨师菜好了没?直到上菜完成.
IO复用型,即阻塞多个I/O操作。在阻塞IO模型中,当有IO操作进行时,系统进程会被调起,这样带来的是效率低,因为一个线程只能处理一个套接字的I/O事件。改进方法就是调用select和poll函数,当监听到有读或写操作的I/O函数时,select函数就会被调用,阻塞多个I/O操作,直到内核监听到有数据返回时,select就反馈数据准备好了,系统再调用recvfrom处理数据。整个过程就好像在餐馆点菜就餐一样,餐馆的客人非常多,但是厨师比较少,厨师一次只能回答一个客人的问题(如菜好了没?),当有了服务员的存在后,我们只需要向服务员点菜、下单、取菜、付费就可以了,服务员就像我们的select函数,监听多个客人的下单、上菜、买单事件,直到整个任务完成。
信号驱动型IO,即通过监听信号来处理IO操作,省去了select函数的阻塞与轮询。整个实现方法是在Socket套接字上安装一个信号处理函数,进行信号驱动I/O,用户进程继续运行并不阻塞。当数据准备好时,进程就会收到一个sigio信号,此时便可调用recvfrom读取数据。整个过程就像在餐馆就餐一样,在餐桌上有智能计菜仪,随时感知做菜的进度,当有菜做好时,智能计菜仪便感知到了,通知服务员或客人取菜。
异步IO模型,即同时并行多件事情。用户进程发起IO操作后,就去做其它的事情了,内核接受到用户进程的消息时,会立刻返回结果并同时等到数据准备完成,再将数据拷贝到用户内存,当所有的操作都完成时,内核给用户进程发送信号告诉它事情已搞定。整个过程就像在餐馆就餐一样,客人下单后,厨师直接接单,当有菜做好时,厨师告诉用户,菜做好了。
在上述我们介绍了五种IO模型,五个IO模型对比如下图所示:
经过本文的讲解,大家对计算机世界的IO掌握了吗?我们的数据经过了九九八十一道关卡,才最终在电脑、iPad、手机等各个终端展现给到用户,每一条数据都不容易,且行且珍惜噢~
喜欢我们的文章吗?还想了解互联网哪些技术,欢迎留言告诉我们
【AI课工场】互联网知识也能如此好玩~
更多热门互联网技术文章抢先知微信公众号【kgc-cn】