流本身实际上只是一个固定大小的FIFO字节队列,可能实现为循环缓冲区。该队列的一侧是您的程序,它使用 fwrite()(或类似方法)将字节放入缓冲区,或者对于输入流,使用 fread() 等从缓冲区中删除字节。
队列的另一边是操作系统软件的某些部分;对于可能是文件系统层的文件;对于套接字,它将是 TCP 堆栈;对于标准输出,它可能是 tty 或伪 tty 驱动程序。在您的程序将一些字节写入流后,另一端的 OS 代码被唤醒,它从 FIFO 的另一端获取这些字节并对其进行适当的处理(例如将它们写入磁盘,或将它们放入 TCP 数据包并将该数据包发送到以太网卡等)
流的内部机制故意尽可能地对您隐藏,这样您的代码就不会依赖于流是如何实现的任何细节。这样,如果操作系统更改,或者您为不同的操作系统重新编译,您的代码(希望)不会中断并需要重写,因为您的代码从未做出任何可能不再正确的假设。
至于语言如何连接到这些操作系统机制,它与您可能使用的任何其他代码库实际上并没有太大区别——就像有人定义了您的代码使用的 fwrite()/fread() API,其他人定义了fread() 和 fwrite() 的实现者可以在其上调用函数的操作系统 API(好吧,实际上它们是系统调用,但就我们的目的而言,它们类似于函数)。并且该操作系统 API 可以反过来调用内核用来与硬件通信的设备驱动程序 API,等等,对于使事情正常工作所需的尽可能多的层。
一路下来都是乌龟:)