fd,重定向与缓冲区
目录
理解一切皆文件
IO的基本过程
重定向
dup2重定向
ls /dev/pts指令用于查询服务器下面的终端,开了几个服务器界面就会有几个终端,而 ptmx
是伪终端主设备(pseudoterminal master)这个不算。
可以从一个终端向另一个中端写入数据。
我们从shell的文件可以打开终端,所以终端又是属于文件的。这也映照了Linux下一切接文件。
每个打开的文件在内核当中都有file对象,保存了文件的inode元素信息。
理解一切皆文件
所有的外设都是文件没问题吧,所以外设都是内容+属性,在属于自己的属性结构体里面保存着信息,今天一个进程通过通过文件描述符表的指针直接调用read或者write函数指针读取所对应fd的文件,那这么多的文件难道需要n多个写或者读函数吗,说明这些read/write函数都是一样的,一样的调用下层的文件属性结构体,那读操作的上层,操作系统另外封装了一层软件层,c语言的底层封装了操作系统的文件操作,所以最上层是glibc(C语言标准库),然后再聊read指针,这么多相同的read指针,进行读取不同的文件,同一个东西可以有不同的状态或者效果,着不就是类似于多态的东西吗,这就是虚拟文件系统,同样的先描述再组织的原则,虽然IO/read/write属性和类别一样但是值可以不同,IO方法又是不同的。
为什么语言喜欢做封装呀,在操作系统都文件函数中如果要以二进制写入数字的话是不行的,这样是会乱码的,所以被剖snprintf转成字符串数组,不同的类型要做不同的转换,在语言层面就没有这个问题,void* 就包含了所有类型了,所以语言喜欢做封装1是方便用户操作,语言封装了才可以在操作系统上进行调用,无论哪种操作系统都是需要兼容语言的,所以2是为了提高语言的可植入性!
IO的基本过程
文件读写过程存在文件内核缓冲区,进程通过struct file的指针找到文件输入或者输出,每个文件都配有操作表,属性集,内核缓冲区,写入文件时会先写入缓冲区,等文件的缓冲区差不多了的时候再一次性写入文件,这个写入文件的过程由OS决定的,修改的本质是先从文件中读取,修改再写入,有时候就需要在文件缓冲区中读取,为什么要有文件缓冲区,统一写入肯定比边输入边就写入快,因为外设比较慢,而输入CPU处理太快了,CPU 速度快,外设慢,文件缓冲区起到了“数据中转站”的作用。减少 I/O 频繁调用,提高系统吞吐量。统一写入可以提高效率。
重定向
进程打开文件,需要给文件分配新的fd,fd的分配规则是使用当前最小的并且没有被使用的fd!
以上fd决对是3,4,5,6了,这是0,1,2都没有被关闭,都在用的情况。
接着我们将1关闭,1是标准输出流吧,printf应该向标准输出流显示器写入对吧,为什么却写入到了文件中,因为标准输出流被重定向了
很显然,第一个自创制的文件的fd被换成1了,那printf是拿着1的去匹配的标准输入流的呀,自然就匹配到文件了。那这个标准输入流不就没有对应的fd了吗,成为没有指针指向的文件,这种没有指向的文件就会被操作系统关掉的,不用了。
这么关闭再重新创建比较不雅观,我们一般重定项会选择使用dup2函数进行完成。
dup2重定向
这个dup2是一个系统调用
重点看dup2,这个函数的意思是将相对于原本的fd拷贝成新的fd,就是用一个新的fd来替换原本的fd,可以看出这个最后一个参数新的fd是我们的目标,将其带入上面的代码关闭的就是,我们要一直使用第一个自我创建的fd来替换原本标准输出流的fd(1),所以目标就是1,相对于替换之后的oldfd就是原本的fd,dup2(fd,1)。
如下:
成功写入文件:原本是直接输出屏幕的。
所以这个替换的机制是什么样的:
原本3指向新打开的文件,现在使用dup2进行替换,就直接拷贝3的指针的地址然后覆盖在1的位置,然后改变指向,指向新打开的文件,这样原本指向标准输出流的指针就不存在了。标准输入流这个文件就没有指针可以指向它了,跟个孤儿一样,最后被操作系统关闭。