vlambda博客
学习文章列表

简单谈谈IO模型这件事,大佬路上的必备知识。

#努力也许不等于成功,可是那段追逐梦想的努力,会让你找到一个更好的自己,一个
沉默努力充实安静的自己。

友情提示:
        关注即可收获更多Linux、嵌入式、深度学习方面知识,阅读本文可收获Linux IO模型的知识,开拓视野。

最近越来越发现,IO操作真的是非常重要,甚至影响了程序的性能,比如自己写的LOG频繁的读写IO。通过使用IO复用,都可以避免使用多线程阻塞的方式实现的东西。

所以今天简单梳理下,因为也是初次总结,不好之处多多包涵。


什么是IO?


这个问题,简单说I指的是input,而O则是output,非常直观的意思就是计算机的输入、输出,描述的是计算机的数据流动的过程。输入/输出是主存和外部设备(如磁盘驱动器、终端、网络)之间拷贝数据的过程。


从操作系统角度来说,IO分为IO调用和IO执行,其中IO调用是由进程发起的,IO执行则是由操作系统来完成。IO调用的目的是将进程的内部数据迁移到外部(输出),或将外部数据迁移到进程内部(输入)。


那么一个进程调用一次外部输入类IO会进行那些步骤呢?

1.进程向操作系统发出请求外部数据

2.操作系统将外部数据加载到内核缓冲区

3.操作系统将数据从内核缓冲区拷贝到进程缓冲区

4.进程读取数据继续后续工作


附一张socket的传输过程图片。

简单谈谈IO模型这件事,大佬路上的必备知识。


针对IO调用(应用进程做的事情)分为阻塞和非阻塞。针对IO执行(操作系统做的事)分为异步和同步。


阻塞IO & 非阻塞IO


阻塞IO指的是,应用进程在请求IO之后,一直等待操作系统的返回,此时应用程序会一直停在请求的位置。


非阻塞IO指的是,应用程序在请求IO之后,不等待操作系统的返回,应用程序直接就返回做其他事情。


单纯的对比两种方式,我们发现好像非阻塞IO好像更好一点,不一直等待,直接返回做其他事情,其实不然。如果我们想要获得读取IO的结果,我们就不得查询它好没好,如果一直使用轮询的方式,其实更加浪费CPU的时间。反而不如使用多线程,使用其中一个线程用来阻塞IO获取数据。


但一个应用程序需要调用多个IO怎么办呢?


不能在一个线程中均阻塞,因为不知道那个IO先被操作系统执行完毕。

开多个线程每个线程都阻塞吗?不是很现实,假如有10几个,光是在线程间切换便浪费不少时间。

那可不可以使用非阻塞IO轮询方式查询?可以考虑,虽然浪费CPU时间,但操作IO比较多还是可以考虑。但我们还有更好的方法,那便是异步IO(asynchronous I/O)。


异步IO & 同步IO


同步IO是应用程序必须等待内核把IO操作处理完成后才会返回。而异步IO则不需等待IO操作完成,而是向内核发起一个操作立刻返回,当内核完成IO操作后,会通过信号的方式通知应用程序。


LINUX IO总共有5种:

同步IO (synchronous IO

异步IO(asynchronous IO

阻塞IO(bloking IO

非阻塞IO(non-blocking IO

多路复用IO(multiplexing IO

信号驱动IO(signal-driven IO


学习这五种IO的操作是很有必要的,比如最近我用的非阻塞IO+多路复用IO,实现了socket 服务端,可以通过epoll IO复用,接收多个客户端连接通信。

另外为了减少访问IO的次数,日志文件也会对IO访问进行控制,并不是简单的产生一条消息就通过IO写入到文件系统,而是先放入缓冲区,等缓冲区写满再往文件系统写的。


另外一般写定时器的时候也是,通过多路复用epoll去监视多个定时器的,而不是一直在阻塞着等待。


通过我的亲身经历告诉大家,这五种IO是必须要学习的,而且还能扩展你的编码思路,让你无拘无束的编码。


#End 2022/4/14 - 21:28