当前位置:   article > 正文

基础IO——文件描述符_文件描述符为什么会打开两次屏幕

文件描述符为什么会打开两次屏幕

1. 文件描述符fd

1.1 open返回值

我们先来看下面的例子:
在这里插入图片描述
运行结果如下:
在这里插入图片描述
我们知道open的返回值小于0为失败,大于等于0为成功。

那么为什么从3开始,0,1,2是什么
原因是:0,1,2默认打开了。
0:标准输入,键盘
1:标准输出,显示器
2:标准错误,显示器

而在C语言中是:
在这里插入图片描述
它们是什么关系呢
首先,FILE是C库提供的结构体。结构体会封装许多成员,而对应文件操作而言,系统接口只认fd。所以,结构体里肯定有fd

证明0,1,2就是标准IO

我们先验证0标准输入:
在这里插入图片描述
运行结果如下:
在这里插入图片描述
然后验证1,2:
在这里插入图片描述
在这里插入图片描述

验证0,1,2和stdin,stdout,stderr的对应关系
在这里插入图片描述
因为stdin,stdout,stderr都是FILE类型的指针,可以用箭头来指向里面的数据。
在这里插入图片描述
可以看到它们就是0,1,2。

2. 理解Linux下一切皆文件

我们知道:一个进程是可以打开多个文件。进程 :打开文件=1:n。所以系统在运行中,可能会存在大量的被打开的文件,而OS肯定会管理这些被打开的文件。而管理就是先描述,再组织

先描述:一个文件被打开,在内核中,要创建被打开的文件的内核数据结构(struct file),里面包含了文件大部分内容和属性
在这里插入图片描述
再组织:对被打开的文件的管理,转化成为了对链表的增删查改

上面说过进程:打开文件=1:n的关系。那么进程如何和打开的文件建立映射关系呢
在这里插入图片描述
在struct task_struct中,有一个指向结构体的指针(struct files_struct *fs)
在这里插入图片描述
在struct files_struct 结构体中,有一个数组,这是一个指针数组,里面指向文件的结构体
在这里插入图片描述
这样我们可以根据下标来找到文件的内容
在这里插入图片描述

0,1,2对应的stdin,stdout,stderr是键盘,显示器,显示器。这些都是硬件,也是用struct file来标识对应的文件的吗

这就要理解Linux下一切皆文件了

第一个问题:如何如何用C语言来实现类呢
我们知道C语言的结构体里是不能写成员方法的,所以我们只能用函数指针。
在这里插入图片描述
如果我们想去调用的话,我们可以这样去传:
在这里插入图片描述
这样我们就可以面向对象的去使用了。

在这里插入图片描述
这是我们的外设设备,每一个外设都有属于自己的读,写方法。
在这里插入图片描述
不同的设备,对应的读写方法一定是不一样的

然后如何去调用这些方法,就和我们上面说的一样。
在这里插入图片描述
所以,当我们想访问某个设备时,操作系统就会创建一个struct file。而在上一层,就是我们刚刚说的进程和struct file的关系了。

3. 文件描述符的分配规则

前面的例子中:默认打开的是3,因为0,1,2已经打开了。

文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符

我们再来看下面的例子:
在这里插入图片描述
我们先把0关闭了。看一下运行结果:
在这里插入图片描述
我们再换成2:
在这里插入图片描述
运行结果:
在这里插入图片描述
我们再换成1:
在这里插入图片描述
运行结果:
在这里插入图片描述
但是1,它什么都不打印了。这是为什么呢?按照文件描述符的分配规则,fd的值一定是1啊。
原因是:一开始我们把1关上了,然后我们open了log.txt文件,那么1就不在指向对应的显示器了。而是已经指向了log.txt的底层struct file对象了

在这里插入图片描述
但是你会发现,log.txt里还是没有。因为我们需要刷新缓冲区。
在这里插入图片描述
在这里插入图片描述
这里,我们要说两个问题:一个是重定向的本质,一个是缓冲区的问题。

4. 重定向的本质

我们先看下面的代码:
在这里插入图片描述
这个把printf换成fprintf,好理解。那么本来应该要往显示器打印,最终却变成了向指定文件打印,这个不就是重定向吗?

正常的情况:
在这里插入图片描述
当我们把1关闭了,然后再打开文件的情况:
在这里插入图片描述
而在C标准库中,stdout里的fd我们天然的设成了1。但是它并不知道1的file对象已经发生了变化,所以还是向1的对象里写入。所以就会写入到新文件里了。

重定向的原理:如果要进行重定向,上层只认0,1,2,3,4…这样的fd,我们可以在操作系统内部,通过一定的方式调整数组的特定下标的内容(指向),我们就可以完成重定向操作了

4.1 使用 dup2 系统调用

在这里插入图片描述
这是一个普通的文件打开操作。如果此时我们要输出重定向到文件里,一定是让3里的file* 的内容复制给1里的file* 。
在这里插入图片描述
所以,最后都和fd里的内容一样了

上面的一堆数据,都是内核数据结构,只有操作系统有权限。所以,操作系统必定会提供接口。那么这个接口就是dup2。
在这里插入图片描述
这个函数的意思是:
在这里插入图片描述
将oldfd复制给newfd, 两个文件描述符指向同一个文件
注意:这里复制的不是下标,而是下标里所指向的内容

所以,根据上面所说的过程,这个函数的参数我们该如何传呢?
答案就是:dup2(fd,1)

在这里插入图片描述
在这里插入图片描述
这样就完成了重定向。

dup2也是有返回值的,但是不常用:返回值: 若dup2调用成功则返回新的文件描述符,出错则返回-1
在这里插入图片描述
这就是当我们输出重定向完成了,如果我们不想要fd,可以把它关了。
在这里插入图片描述
这样,还是可以在1里完成重定向。

4.2 追加重定向

追加重定向,只需改一下标记位就行了:
在这里插入图片描述
运行结果如下:
在这里插入图片描述

4.3 输入重定向

在这里插入图片描述
这是正常的从键盘输入,显示器打印的方式。
在这里插入图片描述
如果我们想输入重定向,那么就不是从键盘读,而是从文件里读。
在这里插入图片描述
我们将0(stdin)重定向。
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/721810
推荐阅读
相关标签
  

闽ICP备14008679号