Linux操作系统学习(文件缓冲区)

缓冲区

什么是缓冲区?

​ 缓冲区(Buffer)就是在内存中预留指定大小的存储空间用来对输入/输出(I/O)的数据作临时存储,这部分预留的内存空间就叫做缓冲区。

缓冲区分为内核缓冲区和用户缓冲区

​ 内核缓冲区是网络传输、文件io等等的“中转”,由操作系统控制,是不可见的,用户必须使用系统提供的接口才能使用到,所以说操作系统不相信任何人,只提供相关接口(read、write等)。

​ 一般每个语言都给用户提供了缓冲区,这里以C语言来举例

​ 在使用C语言的printf函数时,默认是向stdout写入的,那么下面做几个测试:

测试1:stdout的类别是显示器

可以看出,没有\n 打印的hello不会及时向显示器输出,而是最后return时才显示。

测试2:stdout的类别是磁盘文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XsLbTPut-1677869141834)(G:\Typora\图片保存\image-20221208195005918.png)]

可以看出,关闭test,5行hello并没有写入到文件,而不关闭fd 却写入文件里了

首先要知道,stdout的类型是FILE*,FILE是C语言提供的,FILE是一个结构体只不过typedef了。

FILE结构体包括了很多数据,其中就有 缓冲区,以及fd=1.

  • 因为是c语言提供的FILE,所以这个缓冲区属于C语言缓冲区
  • stdout代表显示器,固定去找struct file* array[]中1的位置

测试1和测试2的fprintf是C接口,他们输出的信息都会先缓存到C的缓存区也就是用户缓存区。

测试1没关闭1,此时1的类别属于显示器,那么他就是向显示器输出

  • 当我们在函数结尾没加\n时,它不刷新(注意,man函数的return会刷新缓存,所以最后结束时他会刷新一下)
  • 当我们加了\n时,他就会刷新缓冲区

测试1的fd1表示显示器,向显示器输出的刷新策略是行缓存:换行是刷新缓冲区;

测试2关闭1,之后在代开test.txt文件,根据文件描述符的分配规则,此时1的类别属于磁盘文件,那么就是向文件输出

  • 关闭fd时,向文件输出的字符没有被写入,在结束进程时会刷新缓冲区,但是提前关闭fd就等于“没路了”
  • 未关fd时,反而写入文件中了

测试2的fd1表示磁盘文件,向磁盘文件输出的刷新策略是全缓存:全部缓存区满了才会刷新

​ 最终C语言的缓冲区会通过系统调用接口刷新到文件内核缓冲区,通过文件内核缓冲区刷新到外设(磁盘、显示器等等),而测试2中close(fd)等于是提前把test.txt的fd切断了,C缓冲区无法与文件内核缓冲区建立联系,最后数据就留在了缓冲区

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Zvh9hst-1677869141835)(G:\Typora\图片保存\image-20221208205936953.png)]

注意:内核将缓冲区中的数据“写”到标准输入磁盘文件里,这里“写”不是将缓冲区中的数据移动到磁盘文件里,而是复制到磁盘文件里,也就说此时磁盘文件里保留了一份缓冲区内容的备份

综上,缓冲刷新策略有以下三种:

  • 行缓冲(如向显示器刷新)
  • 全缓冲(如向磁盘文件刷新)
  • 无缓冲(直接写入)

对于测试1和测试2中,可以用fflush(),强制刷新

假如没有缓冲区,我们要从磁盘中读取数据,有几个字符,CPU就要读写几次,CPU是高速率的

例如让打印机打印某个文本,打印机是低速的,CPU是高速的,将数据放入缓冲区,就进行其他的工作了

fork后的缓冲区

假如我们利用fork创建一个子进程,那么子进程的缓冲区是什么样的?

测试:

把输出重定向到文件里,当目标文件发生变化时,操作系统和C语言会自动调整刷新策略

我们知道:

  • 先fork在执行代码,子进程和父进程共享同一份,等于打印两份
  • 先执行代码再fork,本应该不会打印两份,而实际上除了系统接口write的写入(write写到内核缓冲区,由OS刷新到外设中),全都执行了两份

原因:

​ 重定向到文件时,刷新策略变为全缓冲,而父进程的C的缓冲区属于代码数据,是FILE结构体里的一部分,所以要被共享。可是进程在退出时会把用户缓冲区的数据刷新到系统(进程对数据进行写入),父子进程谁先写入谁就会先发生写时拷贝(只要有一个进程刷新用户缓冲区就代表代码数据发生改变),于是子和父进程就各有一份

解决这个问题,我们可以在 fork 前调用 fflush(stdout) 函数强制把父进程缓冲区的数据刷新到系统。
行写入),父子进程谁先写入谁就会先发生写时拷贝(只要有一个进程刷新用户缓冲区就代表代码数据发生改变),于是子和父进程就各有一份

解决这个问题,我们可以在 fork 前调用 fflush(stdout) 函数强制把父进程缓冲区的数据刷新到系统。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值