当前位置: 首页 > news >正文

【Linux】缓冲区的理解

目录

    • 一、实验现象
    • 二、初步认知缓冲区
      • 2.1 缓冲区的刷新策略
      • 2.2 缓冲区在哪里
    • 三、缓冲区模拟实现
    • 四、再次全面理解缓冲区
      • 4.1 用户强制刷新缓冲区(fflush/fsync)


一、实验现象

我们先来看一个现象:

在这里插入图片描述
在显示器中打印内容时,fprintf先打印出来,write后打印出来,这也是非常正常的现象,因为fprintf代码在write之前嘛,但是我们可以看到将1号下标重定向到log.txt,此时write函数的内容先进行打印了??这是什么原因??

下面我们再来看一个奇怪的现象:

在这里插入图片描述

上述实验中fprintf打印了两次,这必定与fork函数有关!!而向显示器中打印内容与向文件中打印内容fprintf与write打印的顺序不一致,fprintf函数是C库函数,而write是系统调用函数,我们初步猜想这必定与两者的内部实现有关系!!事实上这是由于两者的刷新策略不同而导致的,而刷新策略又跟缓冲区有联系,所以下面我们将一步引出缓冲区的概念对它进行着重分析。

二、初步认知缓冲区

实际上我们的数据并不是立马就被显示出来的,我们的C库以及操作系统都会提供一个缓冲区,数据先会被保存在这个缓冲区当中,然后结合一定的刷新策略数据才显示出来!!

缓冲区的本质其实就是充当一个内存暂存数据的地方!!

为什么要引入缓冲区这个概念?有什么意义?我们通过一个例子感性的理解一下:

假设你的朋友小库要生日了,你想在他生日当天送一个礼物给他,但是小库跟你不在同一个地方,小库在美国,而你在湖南。那么你将礼物交给他有两种方式,一种方式是你自己订机票飞往小库的地方将礼物交给他,另外一种方式是去快递点将礼物交给快递公司让它送给小库。那么你会选择哪种方式呢?我想大部分都应该会选择用快递的方式将礼物送给他吧。那么其实快递公司就充当了缓冲区的角色,快递公司会通过它的方式将礼物交给小库,不需要你亲自送给小库,这有什么好处呢?它节省了你的时间,你将礼物交给了快递公司,那么你就可以继续去干你自己的事情,剩下的事交给快递公司就可以了。那么在进程中假设我们调用了fprintf函数,它就会转而将数据拷贝到缓冲区中让缓冲区帮你实现功能,然后立马返回进程继续执行剩下的任务!!

所以为什么要有缓冲区呢?有什么意义?

提高了IO效率,节省了调用者的时间!!

2.1 缓冲区的刷新策略

引出缓冲区的概念之后,那么在缓冲区的数据是如何被使用的呢?这就需要采取一定的刷新策略了。

缓冲区的刷新策略

  • 无缓冲:数据直接传送到目标设备,不经过缓冲区。这种方式的特点是数据传输速度快,但效率低下,容易产生垃圾数据。
  • 行缓冲:数据存储在缓冲区中,当填满一行或遇到换行符时,就会将整行数据一起刷新到目标设备中。行缓冲的特点是占用空间小,效率较高,但无法立即显示输出(当未填满一行或未遇到换行符时会一直存留在缓冲区中,无法立即输出到目标设备中)。显示器采用的刷新策略。
  • 全缓冲:数据存储在缓冲区中,缓冲区填满后,将整个缓冲区一次性刷新到目标设备中。全缓冲的特点是效率高,但需要占用大量的存储空间。普通文件采用的刷新策略。
  • 特殊情况:
    1、进程退出时需要刷新缓冲区:在进程退出时,操作系统会自动关闭该进程的所有文件,并刷新缓冲区,确保数据正确保存。
    2、关闭文件(close)时需要刷新缓冲区:在关闭文件时,操作系统会自动刷新该文件的缓冲区,确保数据正确保存。
    3、用户强制刷新缓冲区:有些操作系统提供了命令或函数可以手动刷新缓冲区,例如Unix/Linux中的sync命令或fsync函数,Windows中的FlushFileBuffers函数等。

上述我说的是基于用户层的缓冲区刷新策略,实际上操作系统的缓冲区刷新策略更为复杂,采用定时刷新、事件触发刷新、混合策略刷新等等,但是大致的思想肯定是一样的,下面我们重点讲解用户层缓冲区的刷新策略就可以了。

行缓冲是现阶段语言层面我们用的最多的一种刷新策略,通过找到\n将缓冲区中的数据刷新出来,这也就是我们通常在printf中带\n数据能立马将缓冲区中的数据刷新出来最终在显示器上看到的原因。有了这个概念,我们就能对printf带\n有了更进一步的了解了,实际上\n不仅仅是为了调整格式,而且能使我们保存在缓冲区的数据刷新出来,最终显示在显示器上!!

为什么显示器通常采用的是行缓冲?

这是为了用户有更好的体验,我们通常希望看到一次一次的现象,而不是把缓冲区填满之后再一次性的刷新缓冲区最终在显示器上只看到一次现象。

对于缓冲区的设计者来说,为了极大的提高IO效率,通常采用的是类似全缓冲的刷新策略,因为需要刷新的数据量非常大,数据将缓冲区填满之后再进行刷新。这样为什么能提高IO效率?

对于显示器文件来说,我们使用的是行缓冲,找到\n就刷新缓冲区,刷新缓冲区的本质就是进行写入,最终将数据写入到显示器文件中,而数据写入外设必定要遵循冯诺依曼体系,换句话说每刷新一次就进行一次IO;而对于全缓冲来说,我们将缓冲区填满之后才刷新一次,不发生刷新的本质,不进行写入,就是不进行IO,不进行调用系统调用,所以fwrite函数调用会非常快,数据会暂时保存在缓冲区中,可以在缓冲区中积压多份数据,最后再统一进行刷新写入,换句话说就是就是一次IO可以IO更多的数据。IO是需要花费时间的,我们通过全缓冲减少了IO的次数,不就是节省了时间,提高了效率吗!!

注:进行IO必定需要访问外设,调用库函数将数据拷贝到缓冲区都不IO,叫拷贝数据!!

2.2 缓冲区在哪里

在之前的实验现象中,我们看到1号下标重定向到log.txt文件后调用write与fprintf,此时在普通文件中打印内容采用的是全缓冲策略,那么fprintf比write先进行调用,但是write比fprintf先进行打印,write是系统调用接口,而fprintf是C库函数。由此说明缓冲区一定不在内核层,所以此时的缓冲区一定是在用户层,它是C库提供的,更具体的来说它在FILE结构体中!!

接下来我们回答一下之前出现的奇怪现象:

用户先调用fprintf函数,实际fprintf会调用fwrite函数将数据先写入缓冲区中,fwrite在这里就充当一个拷贝函数。发生重定向之后我们采用的全缓冲刷新策略,此时"hello fprintf"暂存在缓冲区中,而write它是系统调用接口它的数据根本不放在用户级缓冲区中,而是放在内核层缓冲区中,然后结合操作系统的刷新策略将数据写入到显示器文件,所以它会较fprintf先打印出来。在最后我们使用fork创建子进程,在进程退出前要刷新缓冲区,此时谁先刷新缓冲区就要发生写时拷贝,所以最终我们可以看到两份fprintf打印的内容!!

关于缓冲区的理解到现在还是不够的,我们后续讲完缓冲区的模拟实现之后还会再次进行谈论,到时候我们就能彻底明白缓冲区以及它的刷新策略了。

三、缓冲区模拟实现

下面是我们进行缓冲区的简单模拟实现代码:

mystdio.h

#pragma once#include <stdio.h>#define NUM 1024
#define BUFF_NONE 0x1
#define BUFF_LINE 0x2
#define BUFF_ALL  0x4typedef struct _MY_FILE
{int fd;    // 文件描述符int flags; // flush method, 刷新策略char outputbuffer[NUM]; // 缓冲区int  current;  // 当前指向缓冲区字符的位置
} MY_FILE;MY_FILE *my_fopen(const char *path, const char *mode); // fopen函数
size_t my_fwrite(const void *ptr, size_t size, size_t nmemb,MY_FILE *stream);  // fwrite函数
int my_fclose(MY_FILE *fp); // fclose函数
int my_fflush(MY_FILE *fp); // fflush函数

mystdio.c

#include "mystdio.h"
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <malloc.h>
#include <unistd.h>
#include <assert.h>MY_FILE *my_fopen(const char *path, const char *mode)
{//1. 识别标志位int flag = 0;if(strcmp(mode, "r") == 0) flag |= O_RDONLY;else if(strcmp(mode, "w") == 0) flag |= (O_CREAT | O_WRONLY | O_TRUNC);else if(strcmp(mode, "a") == 0) flag |= (O_CREAT | O_WRONLY | O_APPEND);else {//other operator...//"r+", "w+", "a+"}//2. 尝试打开文件mode_t m = 0666;  // 文件权限int fd = 0;// 如果文件需要不存在需要创建,此时我们就需要设置文件权限if(flag & O_CREAT) fd = open(path, flag, m);else fd = open(path, flag);if(fd < 0) return NULL;//3. 给用户返回MY_FILE对象,需要先进行构建MY_FILE *mf = (MY_FILE*)malloc(sizeof(MY_FILE));// 如果创建结构体失败就关闭文件if(mf == NULL) {close(fd);return NULL;}//4. 初始化MY_FILE对象mf->fd = fd;mf->flags = 0;mf->flags |= BUFF_LINE;  // 默认为行缓冲刷新策略memset(mf->outputbuffer, '\0',sizeof(mf->outputbuffer));  // 初始化缓冲区//mf->outputbuffer[0] = 0; //初始化缓冲区mf->current = 0; // 当前指向缓冲区字符的位置//5. 返回打开的文件return mf;
}void my_fflush(MY_FILE *fp)
{assert(fp);//刷新的本质是将用户缓冲区中的数据,通过系统调用接口,冲刷给OSwrite(fp->fd, fp->outputbuffer, fp->current);fp->current = 0;  // 刷新缓冲区之后将指向缓冲区字符的置为0 -- 起始位置fsync(fp->fd);  // 用户强制将缓冲区的数据刷新到外设中
}size_t my_fwrite(const void *ptr, size_t size, size_t nmemb, MY_FILE *stream)
{// 1. 缓冲区如果已经满了,就先刷新缓冲区if(stream->current == NUM) my_fflush(stream);// 2. 根据缓冲区剩余情况,进行数据拷贝即可size_t user_size = size * nmemb;  // 用户需要传入的字节大小size_t my_size = NUM - stream->current; // 缓冲区剩余字节大小size_t writen = 0;// 如果缓冲区剩余字节大小大于等于用户传入的字节大小,此时将数据拷贝至current之后;反之则最多拷贝my_size字节到current后if(my_size >= user_size){memcpy(stream->outputbuffer+stream->current, ptr, user_size);//3. 更新当前位置stream->current += user_size;writen = user_size;}else{memcpy(stream->outputbuffer+stream->current, ptr, my_size);stream->current += my_size;writen = my_size;}// 4. 开始计划刷新// 全缓冲if(stream->flags & BUFF_ALL){if(stream->current == NUM) my_fflush(stream);}// 行缓冲else if(stream->flags & BUFF_LINE){if(stream->outputbuffer[stream->current-1] == '\n') my_fflush(stream);}// 无缓冲else{//...}return writen;
}void my_fclose(MY_FILE *fp)
{assert(fp);//1. 冲刷缓冲区if(fp->current > 0) my_fflush(fp);//2. 关闭文件close(fp->fd);//3. 释放堆空间free(fp);fp = NULL;
}

main.c

// main.c读者可以自行去进行测试
#include "mystdio.h"
#include <string.h>
#include <unistd.h>#define MYFILE "log.txt"int main()
{MY_FILE *fp = my_fopen(MYFILE, "w");if(fp == NULL) return 1;const char *str = "hello my fwrite";int cnt = 500;//操作文件while(cnt){char buffer[1024];snprintf(buffer, sizeof(buffer), "%s:%d", str, cnt--);//snprintf(buffer, sizeof(buffer), "%s:%d\n", str, cnt--);size_t size = my_fwrite(buffer, strlen(buffer), 1, fp);sleep(1);printf("当前成功写入: %lu个字节\n", size);//my_fflush(fp);}my_fclose(fp);return 0;
}

我们先来看看行缓冲区的现象:

当snprintf带\n时,我们发现数据一次一次的回显在log.txt文件中:

在这里插入图片描述

当snprintf不带\n时,此时默认还是行缓冲刷新,当行缓冲区未满一行或找不到\n时,此时数据在缓冲区中一直积压,直到此时进程结束该缓冲区刷新,此时一下在文件中一次就看到了缓冲区的所有数据:

在这里插入图片描述

如果我们一次一次的回显到文件中,我们可以通过我们自己实现的my_fflush强制刷新缓冲区:

在这里插入图片描述

四、再次全面理解缓冲区

有了上述缓冲区的模拟实现,我们对缓冲区的刷新策略有了更近一步的了解。

接下来我们谈谈用户进行IO的整个流程:

这里以用户使用C语言调用printf打印数据到显示器上为例,在用户层printf会先调用fwrite函数将数据拷贝到用户层缓冲区当中,然后结合一定的刷新策略->通过系统调用(write)将数据拷贝到内核层缓冲区当中,随后操作系统也同样的采取一定的刷新策略将数据最终写入磁盘,所以最终我们就能在显示器文件中看到我们打印的数据了。

我们平时在语言级别中提到的缓冲区其实都是用户层缓冲区,因为内核级缓冲区我们也见不到。

在这里插入图片描述

4.1 用户强制刷新缓冲区(fflush/fsync)

下面我想重点讲讲fflush库函数与fsync系统调用接口:

fflush()函数

fflush()函数是用来刷新标准I/O库的缓冲区的,其中包括用户层的缓冲区。当我们使用C语言中的输出函数(如printf())向文件中写入数据时,这些数据首先被写入用户层的缓冲区中,而不是直接写入磁盘中。只有在缓冲区已满、文件关闭或调用fflush()函数时,才会将缓冲区的数据刷新到内核缓冲区中。那么如果此时我的刷新策略不符合第一二点,此时就使用fflush()函数将数据强制刷新到内核缓冲区中。刷新缓冲区的本质是什么?将用户缓冲区的数据通过系统调用(write)冲刷给操作系统,那么实际上fflush函数强制刷新缓冲区在底层就是通过调用系统调用(write)来完成这项工作的!!

结论:fflush()函数并不会直接将数据写入磁盘,而是将数据写入内核缓冲区中,由操作系统决定何时将数据写入磁盘。

fsync函数

在这里插入图片描述

fsync()函数会强制将一个文件的所有缓冲区数据和元数据(如文件大小、修改时间等)都写入磁盘中,包括文件的用户层缓冲区和内核缓冲区。当fsync()函数返回时,表示数据已经写入磁盘并且已经被持久化保存。这个函数在需要确保数据被持久化保存的场景中非常有用,比如在操作系统崩溃或断电时,而内存具有断点易失的性质,一旦断电文件中所有的数据就丢失了。但是,由于磁盘I/O的性能非常低,所以fsync()函数的执行速度也较慢,尤其是在写入大量数据时。因此,如果需要频繁调用fsync()函数来写入小量的数据,可能会对性能造成一定的影响。一个很常见的例子:我们在CSDN写博客时,我们边写它会边自动保存,实际它就是调用了fsync函数使我们的数据被持久化保存,防止我们突然断网断电导致数据丢失的问题,而且我们也知道有时候这种自动保存的方式是有点卡顿的,这是很常见的问题!!
值得注意的是,对于某些文件系统(如ext3、ext4等),即使使用fsync()函数,操作系统仍然可能会将数据先写入到内存中,然后再在后台将数据写入磁盘中。这种行为被称为延迟写(delayed write)或异步写(asynchronous write),可以提高磁盘I/O的性能。因此,在使用fsync()函数时,不能保证数据一定已经写入磁盘中,只能保证已经被提交到操作系统,并由操作系统尽快写入磁盘。

结论:fsync()函数用于确保数据被持久化保存。


本篇文章的内容就讲到这里了,关于本文如果有任何疑问或者错处欢迎大家评论区相互交流orz~🙈🙈

相关文章:

【Linux】缓冲区的理解

目录 一、实验现象二、初步认知缓冲区2.1 缓冲区的刷新策略2.2 缓冲区在哪里 三、缓冲区模拟实现四、再次全面理解缓冲区4.1 用户强制刷新缓冲区(fflush/fsync) 一、实验现象 我们先来看一个现象&#xff1a; 在显示器中打印内容时&#xff0c;fprintf先打印出来&#xff0c;w…...

基于单片机的电梯控制系统的设计

摘 要: 本文提出了一种基于单片机的电梯控制系统设计 。 设计以单片机为核心&#xff0c;通过使用和设计新型先进的硬件和控制程序来模拟和控制整个电梯的运行&#xff0c;在使用过程中具有成本低廉、 维护方便、 运行稳定 、 易于操作 、 安全系数高等优点 。 主要设计思路是…...

IP-GUARD文档云备份服务器迁移数据操作说明

一、功能简介 使用文档云备份过程可能出现需要迁移旧数据到新目录的情况(如一开始存储目录设置 不合理,之后变更存储目录),下面介绍迁移备份数据到新目录的方法,迁移后可正常查看、 下载、删除原备份文件。 二、同一计算机上迁移存储目录 当仅需要将存储目录迁移到同一计…...

linux常用命令ls详细说明

目录 1.ls的基本功能就是显示当前目录的文件和目录 2.ls输出是按照字母顺序排列的 3.默认不显示隐藏内容&#xff0c;加上参数-a可以显示隐藏的文件和文件夹 4.-R参数可以地柜列出当前目录以及它包含的字目录中的文件 5.-l参数辉显示长列表&#xff0c;也可以显示文件更多信…...

Python3网络爬虫开发实战(4)数据的存储

文章目录 一、文本文件存储1. os 文件 mode2. TXT3. JSON4. CSV 二、数据库存储1. SQLAlchemy2. MongoDB3. Redis1) 键操作2) 字符串操作3) 列表操作4) 集合操作5) 有序集合操作6) 散列操作 4. Elasticsearch1) 检索数据&#xff1a;利用 elasticsearch-analysis-ik 进行分词2)…...

《C++基础入门与实战进阶》专栏介绍

&#x1f680; 前言 本文是《C基础入门与实战进阶》专栏的说明贴&#xff08;点击链接&#xff0c;跳转到专栏主页&#xff0c;欢迎订阅&#xff0c;持续更新…&#xff09;。 专栏介绍&#xff1a;以多年的开发实战为基础&#xff0c;总结并讲解一些的C/C基础与项目实战进阶内…...

每天一个数据分析题(四百五十)- 数据清洗

数据在真正被使用前需进行必要的清洗&#xff0c;使脏数据变为可用数据。下列不属于“脏数据”的是&#xff08;&#xff09; A. 重复数据 B. 错误数据 C. 交叉数据 D. 缺失数据 数据分析认证考试介绍&#xff1a;点击进入 题目来源于CDA模拟题库 点击此处获取答案 数据…...

昇思25天学习打卡营第XX天|Pix2Pix实现图像转换

Pix2Pix是一种基于条件生成对抗网络&#xff08;cGAN&#xff09;的图像转换模型&#xff0c;由Isola等人在2017年提出。它能够实现多种图像到图像的转换任务&#xff0c;如从草图到彩色图像、从白天到夜晚的场景变换等。与传统专用机器学习方法不同&#xff0c;Pix2Pix提供了一…...

数据结构经典测试题5

1. int main() { char arr[2][4]; strcpy (arr[0],"you"); strcpy (arr[1],"me"); arr[0][3]&; printf("%s \n",arr); return 0; }上述代码输出结果是什么呢&#xff1f; A: you&me B: you C: me D: err 答案为A 因为arr是一个2行4列…...

React Native初次使用遇到的问题

Write By Monkeyfly 以下内容均为原创&#xff0c;如需转载请注明出处。 前提&#xff1a;距离上次写博文已经过去了5年之久&#xff0c;诸多原因导致的&#xff0c;写一篇优质博文确实费时费力&#xff0c;中间有其他更感兴趣的事要做&#xff08;打游戏、旅游、逛街、看电影…...

2024西安铁一中集训DAY28 ---- 模拟赛(简单dp + 堆,模拟 + 点分治 + 神秘dp)

文章目录 前言时间安排及成绩题解A. 江桥不会做的签到题&#xff08;简单dp&#xff09;B. 江桥树上逃&#xff08;堆&#xff0c;模拟&#xff09;C. 括号平衡路径&#xff08;点分治&#xff09;D. 回到起始顺序&#xff08;dp&#xff0c;组合数学&#xff09; 前言 T2好难…...

【论文阅读笔记 + 思考 + 总结】MoMask: Generative Masked Modeling of 3D Human Motions

创新点&#xff1a; VQ-VAE &#x1f449; Residual VQ-VAE&#xff0c;对每个 motion sequence 输出一组 base motion tokens 和 v 组 residual motion tokensbidirectional 的 Masked transformer 用来生成 base motion tokensResidual Transformer 对 residual motion toke…...

Mojo控制语句详解

Mojo 包含几个传统的控制流结构,用于有条件和重复执行代码块。 The if statement Mojo 支持条件代码执行语句。有了它,当给定的布尔表达式计算结果为 时,if您可以有条件地执行缩进的代码块 。True temp_celsius = 25 if temp_celsius > 20:print("It is warm.&quo…...

web安全基础学习

http基础 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是一种用于分布式、协作式和超媒体信息系统的应用层协议。本文将介绍如何使用HTTP协议&#xff0c;以及在Linux操作系统中如何使用curl工具发起HTTP请求。 一、HTTP特性 无状态…...

天气预报的爬虫内容打印并存储用户操作

系统名称&#xff1a; 基于网络爬虫技术的天气数据查询系统文档作者&#xff1a;清馨创作时间&#xff1a;2024-7-29最新修改时间&#xff1a;2024-7-29最新版本号&#xff1a; 1.0 1.背景描述 该系统将基于目前比较流行的网络爬虫技术&#xff0c;对网站上&#xff08;NowAPI…...

OrangePi AI Pro 固件升级 —— 让主频从 1.0 GHz 到 1.6 GHz 的巨大升级

前言 OrangePi AI Pro 最近发布了Ascend310B-firmware 固件包&#xff0c;据说升级之后可以将 CPU 主频从 1.0 GHz 提升至 1.6 GHz&#xff0c;据群主大大说&#xff0c;算力也从原本的 8T 提升到了 12T&#xff0c;这波开发板的成长让我非常的 Amazing 啊&#xff01;下面就来…...

学习大数据DAY27 Linux最终阶段测试

满分&#xff1a;100 得分&#xff1a;72 目录 一选择题&#xff08;每题 3 分&#xff0c;共计 30 分&#xff09; 二、编程题&#xff08;共 70…...

ctr管理containerd基本命令

1. 创建命名空间 创建名为custom的命令空间 ctr ns create custom2. 导入镜像 把镜像导入到刚刚创建的空间 ctr -n custom images improt restfulapi.tar3. 创建容器 创建一个test_api的容器 ctr -n custom run --null-io --net-host -d --mount typebind,src/etc,dst/ho…...

rust 初探 -- 路径(path)

rust 初探 – 路径Path 路径&#xff08;Path&#xff09; 目的&#xff1a;为了在 Rust 的模块中找到某个条目&#xff0c;需要使用 路径两种形式&#xff1a; 绝对路径&#xff1a;从 crate root 开始&#xff0c;使用 crate 名或字面值 crate相对路径&#xff1a;从当前模…...

XXE -靶机

XXE靶机 一.扫描端口 进入xxe靶机 1.1然后进入到kali里 使用namp 扫描一下靶机开放端口等信息 1.2扫描他的目录 二 利用获取的信息 进入到 robots.txt 按他给出的信息 去访问xss 是一个登陆界面 admin.php 也是一个登陆界面 我们访问xss登陆界面 随便输 打开burpsuite抓包 发…...

vue2 搭配 html2canvas 截图并设置截图时样式(不影响页面) 以及 base64转file文件上传 或者下载截图 小记

下载 npm install html2canvas --save引入 import html2canvas from "html2canvas"; //使用 html2canvasForChars() { // 使用that来存储当前Vue组件的上下文&#xff0c;以便在回调函数中使用 let that this; // 获取DOM中id为"charts"的元素&…...

请大家监督:我要开启Python之路,首要任务最简单的搭建环境

任务说明&#xff1a; 如上图所示&#xff0c;Python稳稳第一&#xff0c;为何&#xff1f;因为Python可以做很多事情&#xff0c;比如&#xff1a;Web开发&#xff0c;网络爬虫&#xff0c;软件开发、数据分析、游戏开发&#xff0c;金融分析&#xff0c;人工智能与机器学习&a…...

http协议深度解析——网络时代的安全与效率(1)

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 公众号&#xff1a;网络豆云计算学堂 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a; 网络豆的主页​​​​​ 目录 写在前面&#xff1a; 本章目的&#xff1a; …...

类和对象【下】

一、类的默认成员函数 默认成员函数从名字就告诉我们何为默认成员函数&#xff0c;即&#xff1a;用户没有实现&#xff0c;编译器默认自动实现的函数。 这时你不禁一喜&#xff0c;还有这好事&#xff0c;编译器给我打工&#xff0c;那么&#xff0c;我们今天都来了解一下都有…...

面向未来的S2B2C电商供应链系统发展趋势与创新探索

S2B2C电商供应链系统的发展趋势及创新方向。首先分析当前市场环境和消费者需求的变化&#xff0c;如个性化消费、即时配送、绿色环保等趋势对供应链系统的影响。随后&#xff0c;预测并讨论未来供应链系统可能的技术革新&#xff0c;如物联网&#xff08;IoT&#xff09;在物流…...

【C++】哈希容器

unordered系列关联式容器 在之前的博文中介绍过关联式容器中的map与set&#xff0c;同map与set一样&#xff0c;unordered_set与unordered_set也是关联式容器。 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;查询效率可以达到logN&#xff1b;在…...

milvus - VectorDBBench benchmaker 性能测试工具使用经验

IVF_FLAT (Inverted File with Flat Indexing) 优点: 在数据量适中且维度不是非常高的情况下&#xff0c;IVF_FLAT能提供精确的最近邻搜索结果。 相对简单&#xff0c;易于理解和实现。 缺点: 当数据集非常大时&#xff0c;IVF_FLAT需要大量的内存来存储整个数据集&#xff0c;…...

Linux上如何分析进程内存分配,优化进程内存占用大小

云计算场景下,服务器上内存宝贵,只有尽可能让服务器上服务进程占用更少的内存,方才可以提供更多的内存给虚拟机,卖给云客户。 虚拟化三大件:libvirt、qemu、kvm内存开销不小,可以优化占用更少的内存。如何找到进程内存开销的地方直观重要,以qemu为例说明。 一、查看进…...

C语言笔记(第n版):知识清单

注&#xff1a;本文参考自【C reference - cppreference.com】和【C 语言参考 | Microsoft Learn】&#xff0c;颇有点借花献佛的意味…… C 程序是一系列包含声明的文本文件&#xff08;通常为头文件和源文件&#xff09;的序列。它们经过转换成为可执行程序&#xff0c;当操作…...

【香橙派系列教程】(四)基于ARM-Linux架构的语音控制刷抖音项目

【四】基于ARM-Linux架构的语音控制刷抖音项目 文章目录 【四】基于ARM-Linux架构的语音控制刷抖音项目1.语音模块配置1.创建产品2.引脚配置3.词条定义4.添加控制5.发布版本6.烧录固件 2.编程实现语音和开发板通信3.手机接入Linux热拔插1.dmesg命令2.adb调试踩坑问题 3.总结 4.…...