Linux:文件描述符&重定向&缓冲区

标题

      • 文件描述符的本质:
      • 文件描述符的分配方式
      • 文件流指针和文件描述符的区别:
      • 重定向:
    • 重定向函数:
    • 缓冲区
    • > >>是如何转换成重定向的
    • stderr
    • errno

C语言中有一个FILE*指针,也就是文件指针,FILEC是C中提供一个结构体,其内部封装了fd
在C中给一个文件中写入,需要传入一个通过fopen打开的FILE * 指针

如果是给屏幕出输出,则需给fwrite 参数传 FILE*类型的stdout
在系统中给一个文件写入,需要传一个通过open打开的fd文件描述符
如果是给屏幕中输出,则需给write 参数传1

1和2(标准输出和标准错误的区别)

进程给文件中写入,都是在内存中进行的
一个进程可以打开多个文件,OS也要对这些文件进行管理

硬件有自己的读写接口,因为Linux是C语言写的,没有类但是可以模拟,将这些硬件读写函数封装成结构体成员变量(函数指针类型)
每一个硬件的读写口都被封装成同一个类型的结构体,那么就可以用一个数组存储这些相同类型的对象

通过函数指针对OS和驱动层进行解耦

关闭1后,open一个新文件,该文件的fd就是1。这个过程是隐藏的,当再用打印函数给stdout打印时,stdout只单纯向1输入,则最后的输入就到了文件中

文件描述符的本质:

要从内核源码分析,进程运行起来后在内核中是一块task_strck结构体(PCB)

  • PCB中有一个struct files_struct* files的指针,该指针指向对应的结构体struct files_struct;
  • 在这个结构体中有一个fd_array的数组,数组的每一个元素是struct_file* ;
  • 数组的下标称为文件描述符,各元素指向struct file结构体;
  • 这个结构体里保存了文件的元信息(文件名称,文件大小,inode节点号,文件权限,文件所有者,文件所属组,文件时间等信息)
    Linux:文件描述符&重定向&缓冲区_第1张图片

这些信息描述了文件在磁盘中对应的文件

  • 0:标准输入
  • 1:标准输出
  • 2:标准错误
  • 3:默认新打开文件的文件描述符

文件描述符的分配方式

等同于fd_array数组是怎么开辟元素的,给一个元素赋值后,对应的下标才有含义
最小位分配原则,简单的说就是数组有效下标加起来值要最小
在这里插入图片描述

  • 找到当前没有被使用的最小的一个下标,分配给新文件
  • close(0)后,再次查看会将3放在0的位置

将1关闭后,打开新文件后给新文件写入,用printf输出,如果不用fflush刷新,则不会将内容输出到文件中

文件流指针和文件描述符的区别:

我们已经知道文件描述符的实质是数组的下标

文件流指针

  • 文件流指针FILE是C标准库的内容,fopen函数是一个库函数,其返回值是用一个FILE* 类型接收
  • FILE是一个重命名的结构体,在这个结构体中有三个重要的变量:
  • 读缓冲区,写缓冲区,int _fileno
    Linux:文件描述符&重定向&缓冲区_第2张图片

1.读缓冲区有三个指针;
2.写缓冲区;

  • 这两个缓冲区和在进程终止中的exit函数所说的缓冲区一样,之前所说读到刷新缓冲区就是刷新现在所学习的缓冲区;
  • printf把要输出的内容放在了写缓冲区,加上换行符就是让在换行时刷新缓冲区;
  • 通过fileno将内容输入到文件或者屏幕上

3.int _fileno:

  • fileno里面保存的是文件描述符,
  • 所以通过库函数操作文件流指针时,本质上是通过fileno操作内核中的文件描述符

一个文件流指针只能保存一个文件描述符;
一个文件描述可以被多个文件流指针使用
也就是说:

  • 一个进程创建后要想用到标准输入,标准输出,标准错误这三个文件描述符;
  • 等同于要以文件流指针方式使用,需要创建三个文件流指针,每个文件流指针中保存不同的文件描述符

结论:

  1. 文件流指针和文件描述符的关系是:文件流指针当中包含文件描述符
  2. C标准库对应的缓冲区:读缓冲区和写缓冲区
    在exit和_exit区别有一个刷新缓冲区,这个刷新缓冲区就指的是struct _IO_FILE结构体中的缓冲区

重定向:

  • >清空重定向
  • >>追加重定向
  • 重定向符号:为了将原来在屏幕中打印的内容写到文件中
  • echo+内容+重定向符+文件,不写重定向符+文件就直接打印在屏幕上

使用:

  • echo “haha” > 1.text
    查看文件后,内容显示是haha

  • echo “enen” > 1.txt
    查看文件后,内容显示是enen

  • 如果enen使用追加重定向,文件内容就是haha enen
  • 如果直接使用echo +字符串,会直接将该字符串打印到屏幕上

要理解重定向应从文件描述符的角度理解

代码:
Linux:文件描述符&重定向&缓冲区_第3张图片

  • 操作系统创建了一个进程后,进程中有3个文件描述符,标准输入输出错误;
  • 在文件中打开1.txt文件会创建一个3号文件描述符,这个文件描述符也会对于有一个struct_file的结构体;
  • 这4个struct file在磁盘中会对应一个文件,这个文件叫做在dev目录下pts文件夹下的文件3该文件类型是c,设备文件
  • 012文件描述符和3号描述符对应的文件不一样

Linux:文件描述符&重定向&缓冲区_第4张图片

Linux:文件描述符&重定向&缓冲区_第5张图片

  • 3号文件描述符对应的是1.txt文件,若想让在标准输出输出的内容输出到文件中;
  • 就要将1号文件描述符的strcut_file*重新指向3号描述符指向的struct_file中;
  • 也就是输出到1.txt中,此时1号和3号文件描述符都指向1.txt文件,均可操作该文件

以上就是重定向的本质:将文件描述符下标对应元素中的结构体指针指向的结构体更改成为另外的结构体

重定向函数:

dup2(3,1)
重定向:拷贝的是文件描述符中的内容,将3中的内容拷贝到1中去,再关闭3
追加重定向:打开文件的时候,打开方式是append

dup2重定向函数

Linux:文件描述符&重定向&缓冲区_第6张图片Linux:文件描述符&重定向&缓冲区_第7张图片
运行结果:

  • 结果haaaa本应输入到屏幕上,但是重定向后输出到1.txt文件中

缓冲区

缓冲区本质是一段内存

意义

  • 解放使用缓冲区的进程时间
  • 缓冲区可以集中处理数据,减少io次数,提高效率

现象:

  • printf不带\n,数据无法立即刷新
  • write向stdout中写入则立即刷新,所以这个缓冲区不在系统中
  • 刷新之前关闭文件描述符

位置

  • printf内部封装了write
  • printf只能往stdout中打印
  • C语言输出函数中,printf中有个隐藏参数stdout,stdout是file*类型
    file结构体中封装了很多属性,其中有一个是fd,还有一个语言级别的缓冲区

c中每打开一个文件,都要有一个file会返回,所以每个文件都会有一个缓冲区
fopen返回的是file
,fopen会创建file对象,并且返回这个对象的地址

什么时候刷新:
常规情况:

  • 无缓冲区:立即刷新
  • 行缓冲:逐行刷新,有一行内容刷新这一行
    显示器
  • 全缓冲:缓冲区写满
    块设备,磁盘文件

特殊情况:

  • 进程退出
  • 用户强制刷新

Linux:文件描述符&重定向&缓冲区_第8张图片

Linux:文件描述符&重定向&缓冲区_第9张图片

现象:

  • 向显示器写入,打了四行,因为字符串都带的\n,数据都能刷新出来
  • 重定向到文件中后,显示器是行缓冲,磁盘文件中是全缓冲
  • write只有一次,其余函数是两次
  • 这是与fork有关

为什么C函数打印两次

  • 重定向后,数据不会立即刷新,三个C函数打印的内容暂存在对应的stdout的内部
  • 调用fork时创建子进程
  • 父子进程退出后,父子进程刷新缓冲区
  • 刷新的本质是将缓冲区的数据写入到操作系统内部,并清空缓冲区,
  • 缓冲区是在自己file内部维护的,属于进程内部
  • 不管谁刷新,被清空后会发生写时拷贝

printf内部把%d,把整数123,转换成字符,拼接到字符串中,再以字符形式一个个输出

> >>是如何转换成重定向的

>的左边部分是一个命令,右边是文件

stderr

现象:用fprintf,fput分别向stdout和stderr中打印数据,都会把内容打印到屏幕上

  • 重定向到一个文件中后,
    向stderr中打印的内容仍然在屏幕上
    向stdout中打印的内容都打印进了文件里
  • 重定向默认是重定向1
    这样区分谁是正常输出,谁是错误输出

./test > stdout.txt 2>stderr.txt
将向1和2中的内容分别打印到对应的文件中

./test > all.txt 2>&1
把文件描述符中1里的内容拷贝到2中

errno

C语言有一个全局变量,会记录最近一次库函数调用失败的原因
perrno(“库函数”)

strerror函数,把错误码转化成错误码描述

你可能感兴趣的:(Linux系统编程,linux,操作系统)