【Linux】基础I/O——FILE,用户缓冲区

1.FILE里的fd

        FILE是C语言定义的文件结构体,里面包含了各种文件信息。可以肯定的一点是,FILE结构体内一定封装了 fd 。为什么?来看接下来的思路分析:

1.使用系统接口的必然性
         文件存储在磁盘上,属于外设。谁有权限访问外设呢?只有操作系统。因为操作系统对上要提供稳定的服务,对下要管理好各种软硬件资源。
         如果文件操作能绕开操作系统,那么操作系统怎么知道某个文件到底有没有被创建,有没有被销毁呢,还怎么给你提供稳定的服务呢?基于上述简单的认识,我们不难理解,要想访问硬件资源,就必须通过操作系统。
         而操作系统出于安全性和减少使用成本的角度考虑,是不相信任何人的。就像银行一样,不会将金库直接向大众开放,而是只会有几个业务窗口为大家提供服务。操作系统也是这样,操作系统提供的窗口就是系统接口。
         至此通过我们的逻辑推演,我们已经可以得出以下的结论:要想访问外设就必须使用操作系统提供的系统接口。所以C语言的各种文件操作函数本质就是对系统接口的封装

2.FILE结构体封装fd的必然性
 C语言的文件操作都是系统统接口的封装,而系统接口的使用只认fd,因此FILE结构体中必然会封装fd

验证的方法也很简单直接:

FILE究竟是个什么东西呢?是一个c语言提供的结构体类型

我们在/usr/include/stdio.h头文件中可以看到下面这句代码,也就是说FILE实际上就是struct _IO_FILE结构体的一个别名。

typedef struct _IO_FILE FILE;

 而我们在/usr/include/libio.h头文件中可以找到struct _IO_FILE结构体的定义,在该结构体的众多成员当中,我们可以看到一个名为_fileno的成员,这个成员实际上就是封装的文件描述符。

struct _IO_FILE {int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags//缓冲区相关/* The following pointers correspond to the C++ streambuf protocol. *//* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char* _IO_read_ptr;   /* Current read pointer */char* _IO_read_end;   /* End of get area. */char* _IO_read_base;  /* Start of putback+get area. */char* _IO_write_base; /* Start of put area. */char* _IO_write_ptr;  /* Current put pointer. */char* _IO_write_end;  /* End of put area. */char* _IO_buf_base;   /* Start of reserve area. */char* _IO_buf_end;    /* End of reserve area. *//* The following fields are used to support backing up and undo. */char *_IO_save_base; /* Pointer to start of non-current get area. */char *_IO_backup_base;  /* Pointer to first valid character of backup area */char *_IO_save_end; /* Pointer to end of non-current get area. */struct _IO_marker *_markers;struct _IO_FILE *_chain;int _fileno; //封装的文件描述符
#if 0int _blksize;
#elseint _flags2;
#endif_IO_off_t _old_offset; /* This used to be _offset but it's too small.  */#define __HAVE_COLUMN /* temporary *//* 1+column number of pbase(); 0 is unknown. */unsigned short _cur_column;signed char _vtable_offset;char _shortbuf[1];/*  char* _save_gptr;  char* _save_egptr; */_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

2.用户缓冲区

我们看一段代码

例1

#include <stdio.h>
#include<string.h>
#include<unistd.h>int main()
{const char* fstr = "hello fwrite\n";//都是往屏幕打印printf("hello printf\n");fprintf(stdout,"hello fprintf\n");fwrite(fstr, strlen(fstr), 1, stdout);//第二个是块大小,第三个是块个数const char* str = "hello write\n";write(1, str, strlen(str));
}

符合我们的预期

我们将结果重定向到log.txt里面去

嗯?write怎么在前面了?

 我们接着看一下代码

例2

#include <stdio.h>
#include<string.h>
#include<unistd.h>int main()
{const char* fstr = "hello fwrite\n";//都是往屏幕打印printf("hello printf\n");fprintf(stdout,"hello fprintf\n");fwrite(fstr, strlen(fstr), 1, stdout);//第二个是块大小,第三个是块个数close(1);
}

还能打印

我们再看看代码

例3

#include <stdio.h>
#include<string.h>
#include<unistd.h>int main()
{const char* fstr = "hello fwrite\n";printf("hello printf\n");fprintf(stdout,"hello fprintf\n");fwrite(fstr, strlen(fstr), 1, stdout);//第二个是块大小,第三个是块个数const char* str = "hello write\n";write(1, str, strlen(str));fork();
}

打在屏幕也挺正常的

但是,当我们将程序的结果重定向到log.txt文件当中后,我们发现文件当中的内容与我们直接打印输出到显示器的内容是不一样的。

同样一个程序,为什么C库函数printf和fwrite,fprintf打印的内容重定向到文件后就变成了两份,而系统接口write打印的内容还是原来的一份呢?

现在讲不明白,我们先看一些例子

例4

我们把\n都去掉

#include <stdio.h>
#include<string.h>
#include<unistd.h>int main()
{const char* fstr = "hello fwrite";//都是往屏幕打印printf("hello printf");fprintf(stdout,"hello fprintf");fwrite(fstr, strlen(fstr), 1, stdout);//第二个是块大小,第三个是块个数close(1);
}

嗯?为啥啥也没有??

为什么加上\n就有(例2),不加\n就什么也不打印(例4)?

我们接着看例子

例5

#include <stdio.h>
#include<string.h>
#include<unistd.h>int main()
{const char* str = "hello write";write(1, str, strlen(str));close(1);
}

嗯?为什么它能打出来 ?

首先我们要知道这些C式接口——printf,fprintf,fwrite都是调用了系统调用write,但是在例4,例5中只有write打印了出来,为什么?

我们可以这么猜测一下:我们在调用printf,fprintf,fwrite时那些字符串已经写进缓冲区了,而write没有缓冲区,直接打印出来了,而且这个缓冲区还不是系统级的缓冲区

3.为什么要有缓冲区(节省进程IO数据的时间)

        缓冲区是一种用来暂时存储输入或输出数据的内存空间,它可以减少对磁盘或其他低速设备的读写次数,提高计算机的运行效率。缓冲区有三种类型:全缓冲、行缓冲和无缓冲,它们分别在不同的条件下进行实际的I/O操作。缓冲区也可以通过一些函数来设置或刷新。

        我们知道,如果直接将内存中的数据写到磁盘文件中,非常的消耗时间,因为磁盘是外设,外设和内存的速度相比差距非常大,这样子内存直接读取数据的效率就会非常低,这个时候在内存中就会开辟一段空间,这段空间就是缓冲区,进程会将内存中的数据拷贝到缓冲区里,最后再从缓冲区中将数据输入到磁盘外设里。所以缓冲区的意义实际上就是为了节省进程进行数据IO的时间。

        进程将内存中的数据拷贝到缓冲区,这句话可能有些晦涩难懂,但实际上这个工作就是printf,fprintf,fwrite等c式函数做的,与其说printf,fprintf,fwrite等c式函数是写入到文件的函数,倒不如理解成是拷贝函数,将数据从进程拷贝到“缓冲区”或者“外设”中!!!

        此外,缓冲区可以配合格式化输出函数把数据写成正确的格式

4.语言级缓冲区的刷新策略(c缓冲区使用的策略)

        如果有一块数据想要写入到外设中,是一次性将这么多的数据写到外设中效率高,还是将这么多的数据多次少批量的写入到外设中效率高呢?

        答案显而易见,当然是前者,因为相较于CPU和内存的访问速度,外设的访问速度非常的慢的,假设数据output到显示器外设的时间是1s,那么可能990ms的时间都在等待显示器就绪,10ms的时间就已经完成数据的准备工作了,所以访问一个外设是非常辛苦的。

缓冲区一定会结合具体的设备,定制自己的刷新策略:

        语言级缓冲区的刷新策略是指在使用C语言等高级语言进行输入输出操作时,缓冲区何时将数据真正地传送到目标设备或文件的规则。根据不同的设备或文件类型,语言级缓冲区有以下三种刷新策略:

  1. 全缓冲:只有当缓冲区被填满时才进行实际的I/O操作,这种策略一般用于对磁盘文件的读写,可以减少磁盘的访问次数,提高效率。
  2. 行缓冲:只有当在输入或输出中遇到换行符时才进行实际的I/O操作,这种策略一般用于标准输入流(stdin)和标准输出流(stdout),可以保证每行数据都及时地显示或读取,提高用户体验。
  3. 无缓冲:不使用缓冲区,每次输入或输出都直接进行实际的I/O操作,这种策略一般用于标准错误输出流(stderr),可以使得出错信息尽快地反馈给用户,方便调试。

除了以上三种刷新策略外,还有两种特殊情况会导致缓冲区的刷新:

  1. 用户强制刷新:使用fflush函数或类似的操作来显式地清空缓冲区,不管缓冲区是否已满或遇到换行符。
  2. 进程退出时:作为main函数return操作的一部分,缓冲区会被自动刷新,以保证所有数据都被正确地传送到目标设备或文件。

4.1.应用场景

无缓冲

        一般情况下,立即刷新这样的场景非常少,比如显示错误信息的时候,例如发生标准错误的时候,编译器会立即将错误信息输出到显示器文件上,也就是外设当中,而不是将信息先存放到缓冲区当中,应当是立即刷新到显示器文件中。

行缓冲

        我们知道带\n时数据就会立马显示到显示器上,而不带\n时,就只能通过fflush的方法来刷新数据。上面我们所说的缓冲区数据积累满之后在刷新,本身就是效率很高的刷新策略,

那为什么显示器的刷新策略是行缓冲而不是全缓冲呢?

        是因为显示器设备太特殊了,显示器不是给其他设备或机器看的,而是给人看的,而人的阅读习惯就是从左向右按照行来读取,所以为了保证显示器的刷新效率和提升用户体验,那么显示器最好就是按照行缓冲策略来刷新数据。

        如果我们写入数据没有带 \n 就 不发生刷新,也就是不进行写入, 不进行IO ,不进行系统调用 ,所以此时printf,fprintf,fwrite等c式函数函数成本很低,函数调用会非常快,数据暂存在缓冲区里。所以可以在缓冲区积压多份数据,统一进行刷新写入 ,而这个的本质:一次IO可以IO更多的数据,提高IO的效率

全缓冲

        全缓冲的效率毫无疑问是最高的,因为只需要等待一次设备就绪即可,其他刷新策略等待的次数可就不止一次了,在磁盘文件读写的时候,采用的策略就是全缓冲。

5.解答问题

5.1.例题解答

首先我们要明白,那些printf,fprintf,fwrite等c式函数最后都是会调用系统的系统调用接口write

  1. printf,fprintf,fwrite等c式函数是先将数据写到语言级缓冲区里面,然后通过刷新策略刷新数据,然后才会调用系统调用接口write,这样子才能把字符串打印到屏幕上面
  2. 目前的知识水平而言,单独调用write来打印的话,我们先把它看作是没有缓冲区的写方式,一执行就立马打印,例5能证明

很好,我们现在来逐一解答五个例子留下的问题

例1就不说了,我们来看例2,例4

         在例2中, 我们执行这个程序的时候是把数据打印到屏幕上,而将数据打印到显示器时所采用的就是行缓冲,执行printf,fprintf,fwrite等c式函数会先把要打印的字符串拷贝到C缓冲区,但是因为他们每句都有\n,这个会刷新C缓冲区,所以当我们执行完每一句对应代码后就立即将数据刷新并调用系统接口write将字符串写到了显示器上,然后后面才关闭了显示器,完全不会影响到打印。

         在例4中,我们执行这个程序的时候是把数据打印到屏幕上,而将数据打印到显示器时所采用的就是行缓冲,执行printf,fprintf,fwrite等c式函数会先把要打印的字符串拷贝到C缓冲区,但是我们没有给每个要打印的字符串加上换行符\n,然后这些数据就一直堆积在C缓冲区里面,直到进程退出时才会刷新然后调用系统接口write打印出来,但是我们在进程结束之前就关闭了屏幕,所以自然打印不出来了!!!!

        我们接着看例3

        在例3中, 我们将结果打印到屏幕的时候,数据的刷新策略是行缓冲(遇到\n就刷新)然后按照执行顺序先将printf函数要打印的数据写到了C语言自带的缓冲区当中,因为他后面都有\n,所以立刻从缓冲区刷新出来并调用write打印到屏幕上面来了,而fprintf,fwrite也是如此,然后最后执行fork创建子进程,子进程共享父进程的数据,但是父子进程在fork函数之后都没有修改数据的行为,所以这个子进程到进程结束都是和父进程共享代码和数据,也就是说,子进程什么也没做,所以打印结果很符合我们的预期

        而当我们将运行结果重定向到log.txt文件时,数据的刷新策略就变为了全缓冲(等到满了才刷新),此时我们使用printf和fwrite,fprintf函数打印的数据先后都写到了C语言自带的缓冲区当中,等待缓冲区满了/进程退出刷新缓冲区,之后当我们使用fork函数创建子进程时,刚开始是父子进程共享数据和代码,而之后当父进程或是子进程对要刷新C缓冲区内容时要么缓冲区满了,要么哪个进程先退出)本质就是对父子进程共享的数据进行了修改,此时就需要对缓冲区进行写时拷贝,至此C缓冲区就变成了两份,一份父进程的,一份子进程的,所以重定向到log.txt文件当中printf和fwrite,fprintf函数打印的数据就有两份。

        但由于write函数是系统接口,我们可以将write函数看作是没有C缓冲区的,因此write函数打印的数据就只打印了一份。

对于例3的情况,大家可能不相信全缓冲,我们来验证一下

#include <stdio.h>
#include<string.h>
#include<unistd.h>int main()
{const char* fstr = "hello fwrite\n";printf("hello printf\n");sleep(2);fprintf(stdout, "hello fprintf\n");sleep(2);fwrite(fstr, strlen(fstr), 1, stdout);//第二个是块大小,第三个是块个数sleep(2);const char* str = "hello write\n";write(1, str, strlen(str));sleep(5);
}

我们往屏幕打印的结果是

 

我们将其重定向到log.txt里面去

while :; do cat log.txt; sleep 1; echo "*******************" ; done 

 

这说明write没缓冲区可以直接写出来了,但是其他的C库函数都有缓冲区,等到进程结束了才全刷新出来

5.2.这个缓冲区是谁提供的?

        printf和fwrite,fprintf是库函数,write是系统调用,库函数在系统调用的“上层”, 是对系统 调用的“封装”,但是 write 没有缓冲区,而printf和fwrite,fprintf有,足以说明,该缓冲区是二次加上的,又因为 是C,所以是C标准库提供的

        换句话说如果说这个缓冲区是操作系统提供的,那么printf、fputs和write函数打印的数据重定向到文件后都应该打印两次。

5.3.这个缓冲区在哪里?

我们常说printf是将数据打印到stdout里面,而stdout就是一个FILE*的指针在FILE结构体当中还有一大部分成员是用于记录缓冲区相关的信息的。

struct _IO_FILE {int _flags;        /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags//缓冲区相关/* The following pointers correspond to the C++ streambuf protocol. *//* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char* _IO_read_ptr;    /* Current read pointer */char* _IO_read_end;    /* End of get area. */char* _IO_read_base;    /* Start of putback+get area. */char* _IO_write_base;    /* Start of put area. */char* _IO_write_ptr;    /* Current put pointer. */char* _IO_write_end;    /* End of put area. */char* _IO_buf_base;    /* Start of reserve area. */char* _IO_buf_end;    /* End of reserve area. *//* The following fields are used to support backing up and undo. */char *_IO_save_base; /* Pointer to start of non-current get area. */char *_IO_backup_base;  /* Pointer to first valid character of backup area */char *_IO_save_end; /* Pointer to end of non-current get area. */struct _IO_marker *_markers;struct _IO_FILE *_chain;int _fileno;//封装的文件描述符
#if 0int _blksize;
#elseint _flags2;
#endif_IO_off_t _old_offset; /* This used to be _offset but it's too small.  */#define __HAVE_COLUMN /* temporary *//* 1+column number of pbase(); 0 is unknown. */unsigned short _cur_column;signed char _vtable_offset;char _shortbuf[1];/*  char* _save_gptr;  char* _save_egptr; */_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

也就是说,这里的缓冲区是由C语言提供,在FILE结构体当中进行维护的,FILE结构体当中不仅保存了对应文件的文件描述符还保存了用户缓冲区的相关信息。

FILE里还有对应打开文件的缓冲区字段和维护信息

  • 这就说明每个打开的文件都有一个语言缓冲区,通过它自己的文件描述符刷新出来

这个FILE对象属于用户的还是操作系统的?

一定是属于用户级别的 

为什么foen返回FILE*?

 

在系统层调用open,拿文件描述符,在语言层创建出一个FILE对象 

5.4.操作系统有缓冲区吗?

        操作系统实际上也是有缓冲区的,当我们刷新用户缓冲区的数据时,并不是直接将用户缓冲区的数据刷新到磁盘或是显示器上,而是先将数据刷新到操作系统缓冲区,然后再由操作系统将数据刷新到磁盘或是显示器上。(操作系统有自己的刷新机制,我们不必关系操作系统缓冲区的刷新规则)

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

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

相关文章

【AI绘画教程】Stable Diffusion 1.5 vs 2

在本文中,我们将总结稳定扩散 1 与稳定扩散 2 辩论中的所有要点。我们将在第一部分中查看这些差异存在的实际原因,但如果您想直接了解实际差异,您可以跳下否定提示部分。让我们开始吧! Stable Diffusion 2.1 发布与1.5相比,2.1旨在解决2.0的许多相对缺点。本文的内容与理解…

<Rust><GUI>rust语言GUI库tauri体验:前、后端结合创建一个窗口并修改其样式

前言 本文是rust语言下的GUI库&#xff1a;tauri来创建一个窗口的简单演示&#xff0c;主要说明一下&#xff0c;使用tauri这个库如何创建GUI以及如何添加部件、如何编写逻辑、如何修改风格等&#xff0c;所以&#xff0c;这也是一个专栏&#xff0c;将包括tauri库的多个方面。…

filebeat生产环境配置

配置文件属性 生产配置 filebeat.inputs: - type: logenabled: truepaths: - /tmp/logs/*.log- /var/log/system.log- /var/log/wifi.logsymlinks: truejson.keys_under_root: truejson.message_key: xxxjson.add_error_key: true# 如果想卡部分日志&#xff0c;比如用时间作…

开源大语言模型完整列表概览

Large Language Model (LLM) 即大规模语言模型&#xff0c;是一种基于深度学习的自然语言处理模型&#xff0c;它能够学习到自然语言的语法和语义&#xff0c;从而可以生成人类可读的文本。 所谓"语言模型"&#xff0c;就是只用来处理语言文字&#xff08;或者符号体…

R语言优雅的把数据基线表(表一)导出到word

基线表&#xff08;Baseline Table&#xff09;是医学研究中常用的一种数据表格&#xff0c;用于在研究开始时呈现参与者的初始特征和状态。这些特征通常包括人口统计学数据、健康状况和疾病史、临床指标、实验室检测、生活方式、社会经济等。 本人在既往文章《scitb包1.6版本发…

不想填邀请码?Xinstall来帮你,一键安装无忧愁

在这个快节奏的时代&#xff0c;每一个点击都承载着用户的期待与耐心。然而&#xff0c;在下载App的过程中&#xff0c;繁琐的邀请码填写往往成为了用户体验的一大障碍。你是否也曾经因为不愿填写邀请码而放弃了一款心仪的App&#xff1f;今天&#xff0c;就让我们一起走进Xins…

【Unity美术】如何通用3Dmaxs做一个宝箱

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 专栏交流&#x1f9e7;&…

VsCode远程ssh连接失败:Could not establish connection to XXX

一、问题描述 在VsCode中按下"F1"&#xff0c;选择Remote-SSH:Connect to Host 选择一个已经配置好的SSH主机&#xff0c;比如我选择的是192.168.0.104&#xff1a; 结果提示&#xff1a;Could not establish connection to XXX 二、解决方法 观察VsCode的输出信息…

关于山东省首版次中检测报告的要求是什么

关于山东省首版次高端软件申报中的检测报告要求&#xff0c;虽然搜索结果中没有直接提供详细的具体要求&#xff0c;但可以结合一般软件检测报告的标准和要求&#xff0c;以及首版次软件申报的一般流程&#xff0c;推测可能涉及以下几个方面&#xff1a; 1. **检测机构资质**&…

有关电力电子技术的一些相关仿真和分析:⑦三相桥式电压型PWM逆变器与直接/间接法控制单相全桥结构PWM整流器(MATLAB/Siumlink仿真)

1.1 题目一要求 以三相桥式电压型PWM逆变器为对象,研究其在不同调制度下,输出电压的频谱成分变化,依据仿真波形分析其工作时序。 参数要求:三相桥式逆变电路,直流侧电压800V,调制波频率50HZ,开关频率10kHZ,阻感负载R=10Ω,L=5mH。 1.2 题目二要求 以单相全桥结构P…

数据结构之链表操作详解与示例(反转链表,合并链表,旋转链表,对链表排序)

文章目录 1. 反转链表2. 合并链表3. 旋转链表4. 对链表排序总结 链表是一种常见的基础数据结构&#xff0c;它在内存中的存储方式非常灵活。本文将详细介绍反转链表、合并链表、旋转链表以及对链表排序这四种操作&#xff0c;并提供C和C的实现示例。 1. 反转链表 反转链表意味…

可以用EasyConnect连接实验室内网,但无法连接内网才能访问的服务器,为什么?

&#x1f3c6;本文收录于《CSDN问答解答》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&…

自动化产线 搭配数据采集监控平台 创新与突破

自动化产线在现在的各行各业中应用广泛&#xff0c;已经是现在的生产趋势&#xff0c;不同的自动化生产设备充斥在各行各业中&#xff0c;自动化的设备会产生很多的数据&#xff0c;这些数据如何更科学化的管理&#xff0c;更优质的利用&#xff0c;就需要数据采集监控平台来完…

昇思25天学习打卡营第04天|数据变换 Transforms

一、什么是数据变换 Transforms &#xff1f; 通常情况下&#xff0c;直接加载的原始数据并不能直接送入神经网络进行训练&#xff0c;此时我们需要对其进行数据预处理。MindSpore提供不同种类的数据变换&#xff08;Transforms&#xff09;&#xff0c;配合数据处理Pipeline来…

Docker存储目录问题,如何修改Docker默认存储位置?(Docker存储路径、Docker存储空间)etc/docker/daemon.json

文章目录 如何更改docker默认存储路径&#xff1f;版本1&#xff08;没测试&#xff09;版本2&#xff08;可行&#xff09;1. 停止 Docker 服务&#xff1a;2. 创建新的存储目录&#xff1a;3. 修改 Docker 配置文件&#xff1a;4. 移动现有的 Docker 数据&#xff1a;5. 重新…

电脑录屏win10可以用的软件有哪些?分享3款经典的!

在数字化时代&#xff0c;屏幕录制已成为我们工作、学习和娱乐中不可或缺的一部分。无论是教学演示、游戏直播还是软件操作教程&#xff0c;屏幕录制都能帮助我们轻松记录并分享屏幕上的精彩瞬间。那么&#xff0c;对于使用Win10系统的用户来说&#xff0c;有哪些值得推荐的屏幕…

可商用、性能超强!新开源Mamba架构纯代码模型

7月17日&#xff0c;法国著名开源大模型平台Mistral.ai在官网开源了&#xff0c;基于 Mamba架构的纯代码模型——Codestral Mamba。 根据测试数据显示&#xff0c;Codestral Mamba只有70亿参数&#xff0c;但性能却是Meta开源的知名代码模型CodeLlam 7B的两倍&#xff0c;成为…

Chromium源码阅读(9):了解事件跟踪TRACE_EVENT与第三方库Perfetto

Perfetto - System profiling, app tracing and trace analysis Perfetto 是一个用于性能检测和跟踪分析的生产级开源堆栈。它提供用于记录系统级和应用级跟踪的服务和库、本机 Java 堆分析、使用 SQL 分析跟踪的库以及用于可视化和探索多 GB 跟踪的基于 Web 的 UI。 See ht…

基础动态规划题目基础动态规划题目

目录 题目1&#xff1a; P1216 [USACO1.5] [IOI1994]数字三角形 Number Triangles 代码示例&#xff1a; 题目2&#xff1a; Common Subsequence 代码示例 题目3 &#xff1a;最长上升子序列 最长不下降子序列 最长上升子序列oj答案 题目1&#xff1a; P1216 [USACO1.5]…

【ffmpeg命令基础】过滤处理

文章目录 前言过滤处理的介绍两种过滤类型简单滤波图简单滤波图是什么简单滤波示例 复杂滤波图复杂滤波是什么区别示例 总结 前言 FFmpeg是一款功能强大的开源音视频处理工具&#xff0c;广泛应用于音视频的采集、编解码、转码、流化、过滤和播放等领域。1本文将重点介绍FFmpe…