Linux——缓冲区

我在上篇博客留下了一个问题,那个问题就是关于缓冲区的问题,我们发现
文件有缓冲区,语言有用户级缓冲区,那么缓冲区到底是什么?,或者该怎
么认识缓冲区?这篇文章或许会让你有所认识,并且在此之前我还会介绍文
件的结构体对象中的成员。

文章目录

    • 标准错误
    • 文件结构体
  • 1. 缓冲区
    • a. 引入
    • b. 缓冲区存在的意义
    • c. 产生的问题
    • d. C语言提供的缓冲区
  • 2. 尝试实现一个用户缓冲区

但是在此之前,我要补充两个知识点,一个是为什么要有标准错误,而且标准错误也是指向屏幕。还有就是介绍内核数据结构文件结构体。

标准错误

很多人都知道一个进程会默认打开三个文件标准输入,标准输出,标准错误,其中标准输入指键盘,标准输出和标准错误都是指屏幕。我们对标准输入和标准输出很熟悉,基本上每个人都使用过,但是标准错误我们可能没有用过,那么它是什么?以及为什么要有它?
它是一个文件,其实正如它的名字而言,它是记录进程错误信息的文件,那么这个文件可以是屏幕,那自然也可以是一个文本文件。像我们使用过的perror就是输出到标准错误:
在这里插入图片描述
只是我们的默认标准错误指向屏幕而已,当我们把标准错误的指向修改后:
在这里插入图片描述
在这里插入图片描述
这后面的Sucess是C语言中错误码对应的错误信息,当我们修改错误码之后:
在这里插入图片描述
在这里插入图片描述
所以标准错误可以帮助我们在进行大型的工程的时候,通过改变标准错误指向的方式将错误信息写入到特定文本文件以等待后续的处理,而正常的信息则是正常写到自己的目标文件(屏幕)里,互不干扰。

文件结构体

我们说当一个文件被打开时操作系统会创建一个结构体来描述这个文件,那现在我们就来简单认识一下该结构体中的一些成员:
在这里插入图片描述
其中f_list是用来链接系统中被打开文件的。
f_count是有多少个进程打开了文件
f_flags是文件的打开方式
f_mode是文件的权限
f_fowner是说明文件是谁打开的
f_pos表示文件的读写位置
f_mapping跟文件缓冲区有关
f_op是关于对文件操作的操作集:
在这里插入图片描述

1. 缓冲区

我们接下来的缓冲区不做特殊说明说的都是用户级别的缓冲区

a. 引入

我们现在再回顾这个问题:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们看到,有没有fflush,会产生出不同的结果,但是为什么第一份代码中为什么aaaaaaaa没有输入到log.txt文件中呢?这就是我们今天要说的缓冲区。这是因为write是系统调用,它写入内容到一号文件时,是直接写入到文件缓冲区中,而printf是C标准库提供的函数,它会先将内容输入到C语言提供的缓冲区中,但是我们知道,C语言缓冲区中的内容不做处理的话是直到程序运行结束的时候才会刷新,而我们在程序结束之前就将log.txt文件关闭了,那缓冲区中的aaaaaaaaa就被释放了。我们也平常会说斜杠n刷新缓冲区,但是缓冲区到底是什么呢?其实缓冲区就是平台(C语言、操作系统)提供的一块内存而已。

b. 缓冲区存在的意义

那么缓冲区存在的意义是什么呢?我们直接将内容打印到屏幕上不可以吗?当然是可以的,屏幕也是文件,当我们使用系统调用的时候会越过C语言提供的缓冲区,直接写到屏幕上。
首先,我们要对文件进行写入,本质上其实是对硬件的写入,那么假如当我们高频的对硬件进行读写的时候,那势必会将我们程序的运行速度减慢,那么这时侯,缓冲区的作用就来了,我们可以先将对文件的读写操作先写入到这个缓冲区中,然后就不用管它了,而这个缓冲区只需要以一种特定的触发方式,当触发之后再将内容写入到文件中。这么做的话就会分担我们程序的压力,从而提高我们程序的效率。写入文件的动作是必须做的,但是看的就是调取硬件的频率。
并且缓冲区中的触发机制,是缓冲区中的数据积累到一定量的时候,就会触发,从而向文件中写入,不需要多次向文件中写入,那么这也帮我们降低了写入文件的成本,从而变相的提高了我们写入文件的效率
其中的触发机制,就是我们的缓冲区的刷新机制,而缓冲区的刷新机制一般有:
即时缓冲(立即刷新)、行缓冲(行刷新)、全缓冲(缓冲区满了再刷新)。
当然也会有特殊情况:
强制刷新(fflush)、进程退出的时候自动刷新
而对于硬件而言一般来说:
显示器采用的是行刷新,磁盘采用的是全刷新

c. 产生的问题

我们会有这样的一份代码:
在这里插入图片描述
在这里插入图片描述
我们看到,当程序正常运行的时候,结果是符合我们预期的,但是当它重定向到文件的时候,文件中的内容不符合我们的预期。观察它会发现,C标准库的函数打印的内容打印了两份,而系统调用只打印了一份且顺序有所变化。
那么接下来我就带大家分析其中的原因:
程序正常运行我们就不说了,我们只说重定向的问题。
当我们重定向程序打印的内容到文件的时候,其实有一个东西偷偷的改变了,那就是缓冲区的刷新机制,由屏幕缓冲区的刷新到硬盘的刷新机制,而这也是行缓冲到全缓冲的变化。全缓冲(缓冲区是很大的)意味着不强制刷新的情况下,这一点内容是只有进程结束的时候才会刷新缓冲区内容,要注意我们在程序结束之前可是创建了一个子进程父子进程是共享代码数据的,现在就要有一个认识:用户级缓冲区也是属于进程的一份数据,那么缓冲区中的数据父子共享。所以无论父子进程哪个进程先退出,都会将自己缓冲区的内容刷新到文件中,那缓冲的内容是共享的啊,其中一个刷走之后,另一个就没有了,不合理,所以这里会发生写时拷贝,使另一个没有退出的进程仍然享有缓冲区的数据,那这时候,该进程也退出了,也要刷新缓冲区了,这时候会再次向文件中写入数据,而这就是导致重定向的时候C标准库的函数打印了两次的原因,那为什么系统调用没有打印两次呢?那是因为系统调用是直接向文件中写入的,没有经过C语言的缓冲区,也就不是我们程序的数据它已经是操作系统的数据了,不触发写时拷贝,所以就打印一次。
还有是顺序问题,这个很简单:
对于直接运行程序,由于是输出到屏幕,每一个输出的内容又都有斜杠n,所以打印的内容都是即时刷新到文件中了。
对于重定向,由于是全缓冲,系统调用不管直接刷到文件里了,而C标准库的函数还在C语言提供的缓冲区里。所以才会导致顺序发生变化。
在这里插入图片描述
而上面的向文件中写入也只是先写入到文件缓冲区中,然后由操作系统来根据自己的刷新缓冲区的触发机制来刷新缓冲区。
还有一个动词我们要明确,什么是刷新
刷新就是将缓冲区的内容写入到目的地的过程,比如将C语言提供的缓冲区中的内容写到文件缓冲区中,又或者是操作系统将文件缓冲区中的内容写入到磁盘文件中。
而C语言提供的缓冲区这种我们一般就叫做用户缓冲区
上面说的文件缓冲区是属于操作系统的,属于内核缓冲区

d. C语言提供的缓冲区

那么如果上面说的C语言给我们提供了缓冲区的话,它在哪里呢?
我们观察C标准库的关于文件操作的接口就会发现:
在这里插入图片描述
这个缓冲区其实就在FILE结构体里:
在这里插入图片描述
在这里插入图片描述

可以看到,C标准库中的FILE确实是维护着一段空间。

2. 尝试实现一个用户缓冲区

经过上面的探究,我们也试着写出一个属于自己的简单的用户级别的缓冲区,用户级别的缓冲区肯定是封装着系统调用。
头文件

#pragma oncetypedef struct myFILE
{int _fileno; //存储fdchar _buffer[1024]; //用户缓冲区int _end;//缓冲区的最后一个元素的后一个元素的下标
}myFILE;extern myFILE* my_fopen(const char* path, const char* mode);
extern int my_fputs(const char* s, myFILE* stream);
extern int my_fflush(myFILE* stream);
extern int my_fclose(myFILE* fp);

原文件

#include "mylib.h"
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>  
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>myFILE* my_fopen(const char* path, const char* mode)
{int flags = 0;if(strcmp(mode, "r") == 0){flags |= O_RDONLY;}else if(strcmp(mode, "w") == 0){flags |= (O_CREAT|O_WRONLY|O_TRUNC);}else if(strcmp(mode, "a") == 0){flags |= (O_CREAT|O_WRONLY|O_APPEND);}else{}int fd = 0;if(flags & O_RDONLY)fd = open(path, flags);else{fd = open(path, flags, 0666);}if(fd < 0){errno = 2;return NULL;}myFILE* fp = (myFILE*)malloc(sizeof(myFILE));if(fp == NULL){errno = 3;return NULL;}fp->_end = 0;fp->_fileno = fd;return fp;
}int my_fputs(const char* s, myFILE* stream)
{memcpy(stream->_buffer + stream->_end, s, strlen(s));stream->_end += strlen(s);if(stream->_buffer[stream->_end - 1] == '\n'){my_fflush(stream);}return strlen(s);
}int my_fflush(myFILE* stream)
{if(stream->_end > 0);write(stream->_fileno, stream->_buffer, stream->_end);stream->_end = 0;return 1;
}int my_fclose(myFILE* fp)
{my_fflush(fp);close(fp->_fileno);return fp->_fileno;
}
#include <unistd.h>
#include "mylib.h"int main()
{myFILE* fp = my_fopen("log.txt", "w");const char* str = "hello world\n";int i = 0;for(i = 0; i < 20; i++){ my_fputs(str, fp);sleep(1);}my_fclose(fp);return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/293482.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

真实进行软件测试面试中,自动化测试面试到底会问那些?

作者&#xff1a;川石信息 链接&#xff1a;https://www.zhihu.com/question/342170872/answer/813076226 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 自动化测试面试1&#xff1a; 1、使用什么测试框架做的上…

我的应用我做主:扩展线程池

自定义线程创建&#xff1a;ThreadFactory 线程池中的线程是从哪里来的呢&#xff1f; ThreadPoolExecutor(int corePoolSize,//指定了线程池种的线程数量 int maximumPoolSize,//指定了线程池中的最大线程数量。 long keepAliveTime,// 当线程池数量超过了corePoolSize&#x…

pytorch张量的创建

张量的创建 张量&#xff08;Tensors&#xff09;类似于NumPy的ndarrays &#xff0c;但张量可以在GPU上进行计算。从本质上来说&#xff0c;PyTorch是一个处理张量的库。一个张量是一个数字、向量、矩阵或任何n维数组。 import torch import numpy torch.manual_seed(7) # 固…

金蝶Apusic应用服务器 loadTree JNDI注入漏洞复现(QVD-2023-48297)

0x01 产品简介 金蝶Apusic应用服务器是一款企业级应用服务器,支持Java EE技术,适用于各种商业环境。 0x02 漏洞概述 由于金蝶Apusic应用服务器权限验证不当,导致攻击者可以向loadTree接口执行JNDI注入,造成远程代码执行漏洞。利用该漏洞需低版本JDK。(漏洞比较旧,8月份…

ARM作业1

汇编实现三个灯闪烁 汇编代码&#xff1a; .text .global _start _start: 设置GPIOE,GPIOF时钟使能LDR R0,0X50000A28 LDR R1,[R0] ORR R1,R1,#(0x3<<4) STR R1,[R0] 设置PE10,PF10,PE8为输出 LED1LDR R0,0X50006000LDR R1,[R0]ORR R1,R1,#(0X1<<20)BIC R1…

清风数学建模学习笔记--灰色关联分析

一&#xff1a;内容&#xff1a;灰色关联分析 介绍&#xff1a;可以分析多个因素之间&#xff0c;哪一个对y的影响大&#xff0c;哪一个对y的影响小。之前学过回归分析中的标准化回归也可以分析&#xff0c;岭回归&#xff0c;lasso回归等都可以分析&#xff0c;那为什么要使用…

存 储 管 理

(1) 存储管理的任务和功能是什么&#xff1f; 解&#xff1a; 存储管理的主要任务是&#xff1a; 支持多道程序的并发执行&#xff0c;使多道程序能共享存储资源&#xff0c;在互不干扰的环境中并发执行。方便用户&#xff0c;使用户减少甚至摆脱对存储器的管理&#xff0c;使…

Tarjan-vDCC,点双连通分量,点双连通分量缩点

前言 双连通分量是无向图中的一个概念&#xff0c;它是指无向图中的一个极大子图&#xff0c;根据限制条件可以分为边双连通分量和点双连通分量&#xff0c;欲了解双连通分量需先了解Tarjan算法&#xff0c;以及割点割边的概念及求解。本篇博客介绍点双连通分量的相关内容。 前…

Redis取最近10条记录

有时候我们有这样的需求&#xff0c;就是取最近10条数据展示&#xff0c;这些数据不需要存数据库&#xff0c;只用于暂时最近的10条&#xff0c;就没必要在用到Mysql类似的数据库&#xff0c;只需要用redis即可&#xff0c;这样既方便也快&#xff01; 具体取最近10条的方法&a…

千帆 AppBuilder 初体验,不仅解决解决了我筛选简历的痛苦,更是让提效10倍!

文章目录 &#x1f31f; 前言&#x1f31f; 什么是百度智能云千帆 AppBuilder&#x1f31f; 百度智能云千帆 AppBuilder 初体验&#x1f31f; 利用千帆AppBuilder搭建简历小助手&#x1f31f; 让人眼前一亮的神兵利器 - 超级助理 &#x1f31f; 前言 前两天朋友 三掌柜 去北京…

如何搭建Web自动化测试框架?

在程序员的世界中&#xff0c;一切重复性的工作&#xff0c;都应该通过程序自动执行。「自动化测试」就是一个最好的例子。 随着互联网应用开发周期越来越短&#xff0c;迭代速度越来越快&#xff0c;只会点点点&#xff0c;不懂开发的手工测试&#xff0c;已经无法满足如今的…

【计算机系统结构实验】实验5 多核编程(OpenMP编程)

5.1 实验目的 加深对多核处理器架构的理解&#xff1b; 掌握使用OpenMP进行多线程编程的基本方法&#xff1b; 学习Windows和OpenEuler环境下多核编程的过程和time命令&#xff1b; 5.2 实验平台 需要多核处理器的计算机和微软编程工具Visual Studio 2012。Taishan服务器&…