实际场景
- 在操作Linux命令行的时候,如果需要将日志到处并保存到某个文件,我们可能会用到命令
2>&1
,将“正常日志”和“错误日志”都放在一起。 - 在写Java应用时,我们经常会用到
System.out.println(.....)
来打印程序运行时的一些信息,实际上Java还提供了System.err.println(.....)
方法用来打印程序出错时候的信息。
介绍
在Unix系统及类Unix系统中,Standard streams(标准流)是计算机程序与其环境之间,信息输入和输出的通道。包括stdin,stdout和stderr.
根据POSIX协议,这3个标准流都有自己对应的文件描述符(File_descriptor):
文件描述符(整数值) | 名称 | <unistd.h>符号常量 | <stdio.h>文件流 |
---|---|---|---|
0 | Standard input | STDIN_FILENO | stdin |
1 | Standard output | STDOUT_FILENO | stdout |
2 | Standard error | STDERR_FILENO | stderr |
起源
a. 标准流(Standard streams)的起源
在Unix之前的操作系统,程序必须明确指出链接到合适的输入和输出数据。对这当中的许多系统而言,这牵涉一些错综复杂而又与特定操作系统相关的事,过程相当复杂(如控制环境设置、访问一个文件表格、决定区域数据集、和决定读卡器、磁带、磁盘、打印机、打卡机或交互式终端机)。
Unix 的伟大开创贡献之一,就是是提供 抽象设备 :它免除了程序须要知道或在意它正与哪个设备沟通。 Unix 借由数据流的概念来消除这种复杂。数据流是指一种数据字节的有序序列,在其读到文件结尾(End of file, EOF)之前可以一直被读取。另一个 Unix 突破为默认自动链接输入和输出,程序(或者说编写它的程序员),不用为了文件的输入、输出操心。在此之前,程序员需要用复杂的工作控制语言创建链接,协调责任。
b. 文件描述符(File_descriptor)的起源
linux为了实现一切皆文件的设计哲学,不仅将数据抽象成了文件,也将一切操作和资源抽象成了文件,比如说硬件设备,socket,磁盘,进程,线程等。这样的设计将系统的所有动作都统一起来,实现了对系统的原子化操作,大大降低了维护和操作的难度,想想看,对于socket,硬件设备,我们只要读读写写文件就能对其进行操作是多么爽的一件事。
那么在操作这些所谓的文件的时候,我们不可能没操作一次就要找一次名字吧,这样会耗费大量的时间和效率。咱们可以每一个文件操作一个索引,这样,要操作文件的时候,我们直接找到索引就可以对其进行操作了。我们将这个索引叫做文件描述符(file descriptor),在系统里面是一个非负的整数。每打开或创建一个文件,内核就会向进程返回一个fd,第一个打开文件是0,第二个是1,依次递增。
Linux的根目录/dev
是 Device(设备)的缩写。该目录下存放的是 Linux 的外部设备,在 Linux 中访问设备的方式和访问文件的方式是相同的。从该目录下的内容也可以看到标准流的影子
$ ll /dev |grep std
lrwxrwxrwx 1 root root 15 Jul 27 16:54 stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 Jul 27 16:54 stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 Jul 27 16:54 stdout -> /proc/self/fd/1
$ # Linux的文件描述符可以通过 /proc/PID/fd/ 路径获取使用。
应用
- Linux命令行中,可以通过重定向符号(例如:>)来控制标准流的输出,使用实例。
- Java应用当中,也可以通过java.lang.Process类来读取/写入标准流,可参考源码方法注释。
Ref: