Linux——缓冲区

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

文章目录

    • 标准错误
    • 文件结构体
  • 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 once

typedef struct myFILE
{
  int _fileno; //存储fd
  char _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.coloradmin.cn/o/1329919.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单例模式的基本用法

单例模式是众多设计模式中的一种&#xff0c;那说到设计模式&#xff0c;我们要想知道什么是设计模式? 设计模式就是一套反复使用、多数人知晓的、经过分类、代码设计经验总结。 使用设计模式是为了可重用代码、让代码更容易被他人理解&#xff0c;保证代码的可靠性。毫无疑问…

Oracle WebLogic Server WebLogic WLS组件远程命令执行漏洞 CVE-2017-10271

Oracle WebLogic Server WebLogic WLS组件远程命令执行漏洞 CVE-2017-10271 已亲自复现 漏洞名称漏洞描述影响版本 漏洞复现环境搭建漏洞利用 修复建议 漏洞名称 漏洞描述 在Oracle WebLogic Server 10.3.6.0.0/12.1.3.0.3/2.2.1/1.10/12.2.1.1/22.0&#xff08;Application …

Pipelined-ADC设计二——结构指标及非理想因素(Part1)

本章将详细介绍电路各个模块的设计思路和设计中需要注意的关键点&#xff0c;给出流水线ADC中的非理想因素&#xff0c;并计算出流水线ADC各个模块具体指标。根据电路中信号的传输方向&#xff0c;依次介绍采样保持电路、Sub_ADC&#xff0c;MDAC 等模块的设计。&#xff08;本…

【GitHub精选项目】短信系统测试工具:SMSBoom 操作指南

前言 本文为大家带来的是 OpenEthan 开发的 SMSBoom 项目 —— 一种用于短信服务测试的工具。这个工具能够发送大量短信&#xff0c;通常用于测试短信服务的稳定性和处理能力。在合法和道德的范畴内&#xff0c;SMSBoom 可以作为一种有效的测试工具&#xff0c;帮助开发者和系统…

关于redis单线程和IO多路复用的理解

首先&#xff0c;Redis是一个高性能的分布式缓存中间件。其复杂性不言而喻&#xff0c;对于Redis整体而言肯定不是只有一个线程。 我们常说的Redis 是单线程&#xff0c;主要是指 Redis 在网络 IO和键值对读写是采用一个线程来完成的&#xff0c;这也是 Redis 对外提供键值存储…

【ARMv8M Cortex-M33 系列 1 -- SAU 介绍】

文章目录 Cortex-M33 SAU 介绍SAU 的主要功能包括SAU 寄存器配置示例 Cortex-M33 SAU 介绍 在 ARMv8-M 架构中&#xff0c;SAU&#xff08;Security Attribution Unit&#xff09;是安全属性单元&#xff0c;用于配置和管理内存区域的安全属性。SAU 是 ARM TrustZone 技术的一…

论文阅读——Flamingo

Flamingo: a Visual Language Model for Few-Shot Learning 模型建模了给定交织的图片或支视频的条件下文本y的最大似然&#xff1a; 1 Visual processing and the Perceiver Resampler Vision Encoder&#xff1a;from pixels to features。 预训练并且冻结的NFNet&#xff…

kindeditor The method toJSONString() is undefined for the type JSONObject

kindeditor 插件上传文件出错的 json_simple-1.1.jar 也不知道是多老的项目&#xff0c;多老的包了&#xff0c;稀有东西

助力智能人群检测计数,基于DETR(DEtectionTRansformer)开发构建通用场景下人群检测计数识别系统

在一些人流量比较大的场合&#xff0c;或者是一些特殊时刻、时段、节假日等特殊时期下&#xff0c;密切关注当前系统所承载的人流量是十分必要的&#xff0c;对于超出系统负荷容量的情况做到及时预警对于管理团队来说是保障人员安全的重要手段&#xff0c;本文的主要目的是想要…

Liteos移植_STM32_HAL库

0 开发环境 STM32CubeMX(HAL库)keil 5正点原子探索者STM32F4ZET6LiteOS-develop分支 1 STM32CubeMX创建工程 如果有自己的工程&#xff0c;直接从LiteOS源码获取开始 关于STM32CubeMX的安装&#xff0c;看我另一篇博客STM32CubeMX安装 工程配置 创建新工程 选择芯片【STM32F…

C++:第九讲前缀和与差分

Everyday English Your optimal career is simply this: Share the real you with physical world through th e process of creative self-expression. 你的最佳职业很简单&#xff0c;就是这样&#xff1a;通过创造性自我表达的途径和世界分享真实的你。 前言 这节课带你们…

Codeforces Round 862 (Div. 2)

Problem - A - Codeforces AC代码: #include<bits/stdc.h> #define endl \n //#define int long long using namespace std; const int N1e310; int a[N]; int n; void solve() {cin>>n;int ans0;for(int i1;i<n;i) cin>>a[i],ans^a[i];if(n%21){for(in…

3.[BUUCTF HCTF 2018]WarmUp1

1.看题目提示分析题目内容 盲猜一波~ &#xff1a; 是关于PHP代码审计的 2.打开链接&#xff0c;分析题目 给你提示了我们访问source.php来看一下 大boss出现&#xff0c;开始详细手撕~ 3.手撕PHP代码&#xff08;代码审计&#xff09; 本人是小白&#xff0c;所以第一步&…

Linux Centos-7.5_64bit 等保测评

一、新增用户 新增test用户 useradd test 设置密码 passwd 修改test的密码 passwd test 修改/etc/sudoers文件&#xff0c;找到下面一行&#xff0c; /etc/sudoers test ALL(ALL) ALL 保存是出现 E45: readonly option is set (add ! to override) 解决办法&#xff…

arduino舵机练习

接地线gnd和电源线5v&#xff1b;信号线链接任意数字针脚 // C code // #include <Servo.h> //引入舵机库Servo servo_2; //定义舵机void setup() {servo_2.attach(2, 500, 2500);/* servo_2.attach(2, 500, 2500) servo_2 对象的一个方法调用&#xff0c;其中包含…

【Amazon 实验①】使用Amazon WAF做基础 Web Service 防护

文章目录 一、实验介绍二、实验环境准备三、验证实验环境四、Web ACLs 配置 & AWS 托管规则4.1 Web ACLs 介绍4.2 Managed Rules 托管规则4.3 防护常见威胁类型&#xff08;sql注入&#xff0c;XSS&#xff09;4.4 实验步骤4.4.1 创建Web ACL4.4.2 测试用例4.4.3 测试结果4…

【Spring实战】配置多数据源

文章目录 1. 配置数据源信息2. 创建第一个数据源3. 创建第二个数据源4. 创建启动类及查询方法5. 启动服务6. 创建表及做数据7. 查询验证8. 详细代码总结 通过上一节的介绍&#xff0c;我们已经知道了如何使用 Spring 进行数据源的配置以及应用。在一些复杂的应用中&#xff0c;…

Linux的/proc/self/学习

文章目录 /proc目录/proc/self的使用 在做SSTI模板注入的CTF题中&#xff0c;发现有师傅提到可以用/proc/self这个目录获取flag&#xff0c;所以也来学习一波主要参考. (我才知道&#x1f601;&#x1f601;&#x1f601;)可以通过/proc/$pid/来获取指定进程的信息&#xff0c…

uniapp uview 页面多个select组件回显处理,默认选中

<view class"add-item column space-around" click"selectClick(1)"><text class"w-s-color-3 f-28">商品分类</text><view class"w-100 space-between"><!-- 第一个参数为你的单选数组&#xff0c;第二个…

Laravel框架使用phpstudy本地安装的composer用Laravel 安装器进行安装搭建

一、首先需要安装Laravel 安装器 composer global require laravel/installer 二、安装器安装好后&#xff0c;可以使用如下命令创建项目 laravel new sys 三、本地运行 php artisan serve 四、 使用Composer快速安装Laravel5.8框架 安装指定版本的最新版本&#xff08;推荐&a…