当前位置: 首页 > Linux

关于“流和管道”的一些小事

时间:2023-04-06 04:10:06 Linux

“流”的概念在开发中很常见。java语言中的InputStream和OutputStream我们再熟悉不过了,node中的WriteStream和ReadStream,cpp中的stream……似乎这是一门编程语言。不可或缺的部分。初学者一般都是按照文档来完成我们的程序,但对流程本身并没有特别的了解。如果你是专业出身,老师会告诉你,“流”,顾名思义,就像一股水流,从一端流向另一端。那么流能为我们提供什么?它为什么存在?让我们举一个简单的流的例子。如果我们需要顺序读写一个文件的内容(非随机读写),肯定会用到流。我们可以打开一个输出流,该流以文件数据为源。开发或代码为输出形成一个管道。在代码的那一端我们可以通过read操作来获取文件内容。这是将水从一个桶输送到另一个桶的典型过程。这个过程好像没什么神奇的,我们就靠他完成简单的读写操作。如果这时候,我们有一个算法可以根据一个或一个字节进行压缩和解压呢?我们可以先从文件流中读取几个字节,经过算法处理后输出处理结果,这样外部算子其实是察觉不到的——开发者还是“拿着桶接水”,不管“水”是“纯净水”或其他东西。那么,这个操作给我们带来了一种可能,就是流的变换(Transform)。就好像在源端和我们的接收端之间加了一些中间件,拆掉我们的管道,连接中间件的管道,不断的转换每一个流过的字节。没错,刚才我们提到的压缩算法大家一定很熟悉了,就是gzip算法。流的转换就是基于这个Transform的应用。我们经常在node.js中看到一个叫做pipe的方法。对于流的操作,我们可以使用pipe的方式串联起来,像这样fs.createReadStream(...).pipe(gzip()).pipe(other()).pipe(fs.createWriteStream(...))这种基于流处理的声明式编程范式让我们的业务流程变得非常清晰。如果把中间过程比作“如果水被染色了,那么我们的管道更像是一个提供“染色功能”的管道。为什么要强调这一点呢?我们可以想象,如果我们给水染色,我们不需要先把所有的水放到一个桶里,然后往桶里加入染色剂,再把染过色的水倒进另一个管道。也就是说,我们的面向流的操作在很多方面会比一般的方案节省更多的内存cases!显然我们有很多方法可以做到,我们可以使用少量的内存(缓存),例如当我们需要将4个字节转换为1个Int时,我们可能需要一个函数(想想BufferedInputStream/BufferedOutputStream)流的最小单位是字节,但是我们可以使用缓存把流处理变成块(帧)的处理方式,处理后可以变成另一个流,管道流的容器其实就是一个管道。pipeline可以有Buffer也可以没有,如果需要处理一段数据,一般都需要一个buffer。注意这里block的概念,block一般是一组有边界或者固定长度的字节。这里我们再做一个想象。将WriteStream和ReadStream视为管道的两端。管道中可能有缓冲区。管道最基本的数据类型是字节。管道的两端可以任意连接到其他管道。管道会对传递过来的数据做一些处理,或者修改字节,或者类型转换。那么这样一套模型就是我们的流水线编程模型。管道并不是一个特别抽象的概念。它们在操作系统中很常见。最经典的就是我们的标准输入和标准输出。标准输入是一个写端口,管道的另一端连接到进程。我们在写端写一些数据,进程可以读到。这样我们就可以达到进程间通信(IPC)的目的。如果一个进程接收标准输入并执行标准输出,那么我们进程间通信的成本就会很低——只需将目标进程的StdIO重定向到我们生成的管道即可。OS中的Stream和I/O我们已经了解到pipeline和stream是一个整体的概念,那么我们经常遇到的OS级的stream编程模型有哪些呢?其实很常见,包括我们前面介绍的,所以主要有以下几种:标准输入输出(stdio)文件管道(匿名管道和命名管道)Socket(网络套接字)tty(终端)unix本地套接字那么对于这些??文件,我们最常听到的名词就是I/O。在unix世界中,所有的流都是以文件描述符(FileDescriptor缩写为fd)的形式来描述的。在C语言编程的过程中,我们可能对这个名词非常熟悉。我们好像听过一句话:“Linux把所有可读可写的东西都比作文件”,大概就是这个意思。我们可以使用linux提供的open函数获取fd,然后使用read和write对fd进行操作。一旦你封装了多次读写,就变成了我们之前介绍过的streamtransformer。Linux提供的对I/O的系统调用是我们流概念的基石。I/O模型事实上,如果我们想要以优雅的范式编写代码,我们可能需要对I/O模型做一些了解。三大操作系统中,各自都有自己最优的I/O模型来实现OSI/O模型WindowsIOCP(完成端口)LinuxepollmacOS(Darwin)kqueue之间有很多相似之处,如果你为特定的对象提供I/O操作操作系统,那么你应该选择系统下最好的机型来做,这样才能最大化你的I/O效率(流式读写效率),本文暂且不赘述,有需要的同学想知道的可以自行查阅相关资料。