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 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.mfbz.cn/a/264773.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ONE·MySQL || 基础介绍】

总言 主要内容&#xff1a;MySQL在Centos 7下的安装&#xff08;主要学习相关指令语句&#xff0c;理解安装操作是在做什么&#xff09;、对MySQL数据库有一个基础认识。 文章目录 总言0、MySQL的安装与卸载&#xff08;Centos 7&#xff09;0.1、MySQL的卸载0.1.1、卸载不必要…

MySQL数据库 触发器

目录 触发器概述 语法 案例 触发器概述 触发器是与表有关的数据库对象&#xff0c;指在insert/update/delete之前(BEFORE)或之后(AFTER)&#xff0c;触发并执行触发器中定义的soL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性&#xff0c;日志记录&am…

XUbuntu22.04之跨平台音频编辑工具(平替Audition):ocenaudio(二百零二)

加粗样式 简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#…

FastDDS编译

Windows installation from binaries download 里面有二级菜单 view 安装后切换至目录 C:\Program Files\eProsima\fastrtps 2.12.1\examples\cpp\dds cmd打开此目录 mkdir build cd build cmake … //cmake …可以换成下面这句&#xff0c;这是官网的推荐 cmake -Bbuilde…

nn.LSTM个人记录

简介 nn.LSTM参数 torch.nn.lstm(input_size, "输入的嵌入向量维度&#xff0c;例如每个单词用50维向量表示&#xff0c;input_size就是50"hidden_size, "隐藏层节点数量,也是输出的嵌入向量维度"num_layers, "lstm 隐层的层数&#xff0c;默认…

R语言中使用ggplot2绘制散点图箱线图,附加显著性检验

散点图可以直观反映数据的分布&#xff0c;箱线图可以展示均值等关键统计量&#xff0c;二者结合能够清晰呈现数据蕴含的信息。 本篇笔记主要内容&#xff1a;介绍R语言中绘制箱线图和散点图的方法&#xff0c;以及二者结合展示教程&#xff0c;添加差异比较显著性分析&#xf…

[Angular] 笔记 6:ngStyle

ngStyle 指令: 用于更新 HTML 元素的样式。设置一个或多个样式属性&#xff0c;用以冒号分隔的键值对指定。键是样式名称&#xff0c;带有可选的 .<unit> 后缀&#xff08;如 ‘top.px’、‘font-style.em’&#xff09;&#xff0c;值为待求值的表达式&#xff0c;得到…

KingbaseV8R6单实例定时全量备份步骤

此场景为单机数据库节点内部备份&#xff0c;方便部署和操作&#xff0c;但备份REPO与数据库实例处于同一个物理主机&#xff0c;冗余度较低。 前期准备 配置ksql免密登录(必须) 在Kingbase数据库运行维护中&#xff0c;经常用到ksql工具登录数据库&#xff0c;本地免密登录…

钓鱼与木马实践(仅供参考不可实践)

声明:内容仅供学习&#xff0c;请勿违法使用&#xff0c;违者后果自负 一.部署云服务器 购买一台云服务器,Windows&#xff08; 中文 &#xff09;版本即可 华为云官网&#xff1a;https://www.huaweicloud.com/ 登录后进入控制台购买完成后远程登录云服务器 二.部署WEB运行…

Zookeeper的学习笔记

Zookeeper概念 Zookeeper是一个树形目录服务&#xff0c;简称zk。 Zookeeper是一个分布式的、开源的分布式应用程序的协调服务 Zookeeper提供主要的功能包括&#xff1a;配置管理&#xff0c;分布式锁&#xff0c;集群管理 Zookeeper命令操作 zk数据模型 zk中的每一个节点…

springMVC-自定义拦截器

一、先来看一个需求 Spring MVC也可以使用拦截器对请求进行拦截处理&#xff0c;用户可以自定义拦截器来实现特定的功能&#xff0c;比如对临时文件的清除&#xff0c;或者对某些ip地址进行拦截器. 二、springMVC自定义拦截器介绍 (1)需要实现一个接口 HandlerInterceptor. (…

大创项目推荐 深度学习+python+opencv实现动物识别 - 图像识别

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 inception_v3网络5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…

Stable-diffusion-webui本地部署和简要介绍

Stable Diffusion 是一款基于人工智能技术开发的绘画软件&#xff0c;它可以帮助艺术家和设计师快速创建高品质的数字艺术作品。是2022年发布的深度学习文本到图像生成模型。它主要用于根据文本的描述产生详细图像&#xff0c;同时也可以应用于其他任务&#xff0c;如内补绘制、…

MySQL数据库 视图

目录 视图概述 语法 检查选项 视图的更新 视图作用 案例 视图概述 视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的。 通俗的讲&#xff0c;视图只保存…

eventbus,在this.$on监听事件时无法在获取数据

问题&#xff1a;vue中eventbus被多次触发&#xff0c;在this.$on监听事件时&#xff0c;内部的this发生改变导致&#xff0c;无法在vue实例中添加数据。 项目场景 一开始的需求是这样的&#xff0c;为了实现两个组件(A.vue ,B.vue)之间的数据传递。 页面A&#xff0c;点击页面…

ffmpeg 硬件解码零拷贝unity 播放

ffmpeg硬件解码问题 ffmpeg 在硬件解码&#xff0c;一般来说&#xff0c;我们解码使用cuda方式&#xff0c;当然&#xff0c;最好的方式是不要确定一定是cuda&#xff0c;客户的显卡不一定有cuda&#xff0c;windows 下&#xff0c;和linux 下要做一些适配工作&#xff0c;最麻…

Linux创建目录命令@mkdir

目录 命令原型概念作用命令不带参数命令带参数 总结 命令原型 mkdir [ -p ] Linux路径 》参数必填&#xff0c;表示Linux路径&#xff0c;即要创建的文件夹的路径&#xff0c;相对路径或绝对路径均可 》 -p 选项可选择写或不写。-p 表示自动创建不存在的父目录 &#xff08;创…

左值右值引用,完美转发

1.c98/03&#xff0c;类模板和函数模板只能含固定数量的模板参数&#xff0c;c11的新特性可以创建接受可变参数的函数模板和类模板 //Args是一个模板参数包&#xff0c;args是一个函数形参参数包 //声明一个参数包Args… args,这个参数包可以包括0到任意个模板参数 template&l…

成功案例分享:物业管理小程序如何助力打造智慧社区

随着科技的进步和互联网的普及&#xff0c;数字化转型已经渗透到各个行业&#xff0c;包括物业管理。借助小程序这一轻量级应用&#xff0c;物业管理可以实现线上线下服务的无缝对接&#xff0c;提升服务质量&#xff0c;优化用户体验。本文将详细介绍如何通过乔拓云网设计小程…

如何确保游戏翻译的质量

随着全球化的加速和游戏行业的国际化&#xff0c;越来越多的玩家开始接触并喜欢玩国际游戏。然而&#xff0c;由于语言障碍&#xff0c;很多玩家无法理解游戏中的文本和对话&#xff0c;这严重影响了游戏体验。因此&#xff0c;游戏翻译变得尤为重要。那么&#xff0c;如何确保…