【Linux】学习-基础IO—下

Linux基础IO—上

重定向

通过上篇的学习,我们了解了文件描述符的分配规则是遍历指针数组,用没有被使用的最小下标作为新的文件描述符,也就是我们可以通过关闭三个标准流文件并使用他们原先所占用的0,1,2描述符。

那我们假设这样一种情况,我们向显示器中打印内容,但在此之前我们将显示器文件,也就是标准输出流关闭,然后再创建并打开一个新的文件,此时我们向显示器中打印的内容会到哪去呢?

int main()
{close(1);int fd = open("myfile", O_WRONLY | O_CREAT, 00644);if (fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);fflush(stdout);close(fd);exit(0);
}

image-20230920214947600

运行之后我们发现,无论运行多少变显示器上都不会打印出内容,但是通过查看文件内容发现本来应该输出到显示器的内容被写到了文件里

这种现象就叫输出重定向!

注意:printf默认输出的文件就是stdout,而进程默认会打开三个标准流,每次都是如此,并且标准输出流文件描述符也总是1。

也就是说,像printf,scanf,fprintf,fscanf这些函数在底层只认识文件描述符,比如stdout对应的文件描述符是1,他们只认识1,而不认识什么stdout,是通过stdout建立起联系的

因此,重定向的本质,其实是在OS内部,更改fd对应的文件指向!

接下来我们再看个追加重定向的例子,也就是先关闭文件描述符为1的文件,再以追加的方式打开一个文件,这时系统自动分配,完成重定向!

int main()
{close(1);int fd = open("myfile", O_WRONLY|O_APPEND|O_CREAT);if (fd < 0){perror("open");return 1;}printf("hello append,my fd is: %d\n", fd);printf("hello append,my fd is: %d\n", fd);printf("hello append,my fd is: %d\n", fd);fflush(stdout);close(fd);exit(0);
}

image-20230921100805532

这就是追加重定向,但我们使用的时候,我们利用的是文件描述符的分配规则来完成的,也就是在打开文件前先一步关闭对应的想重定向的文件,这样的话导致了代码使用时非常不灵活,因此能不能有一种灵活的方法能够随时重定向呢?系统就给我们一个接口:dup2函数——duplicate file description

dup2函数

其实完成重定向的本质就是把fd_array中对应的内容覆盖掉,因为数组下标是固定的,只需要将需要完成重定向的文件描述符覆盖掉即可完成,在系统中就用这样的一个接口来帮助我们完成这件事

画图:占位

没什么比直接阅读man手册实在!

image-20230921101208937

newfd是oldfd的拷贝,也就是后者原本fd_array数组对应的fd下标里的内容如今换成了前者的内容,如果前者原本对应的内容不是有效的内容,则调用失败,而后者的文件并不会被关闭。必要时可以先关闭newfd对应的文件。

例如,我们要以上面举过的例子,我们要将原本输出到显示器上的内容重定向到文件中,那么oldfd对应的应该就是我们打开的文件对应的文件描述符fd,而newfd对应的就是显示器原本对应的内容1,这样子我们可以任意时候打开文件并任意时候完成重定向而不用预先关闭,非常灵活方便!

// test:  dup2
int main()
{int fd = open("./log", O_CREAT | O_RDWR,0666);if (fd < 0){perror("open");return 1;}close(1);dup2(fd, 1);for (;;){char buf[1024] = {0};ssize_t read_size = read(0, buf, sizeof(buf) - 1);if (read_size < 0){perror("read");break;}printf("%s", buf);fflush(stdout);}return 0;
}

image-20230921104802285

运行程序后可以明显发现我在键盘上敲入的内容并没有显示到显示器上,而查看文件内容后发现:

image-20230921104813285

已经被重定向到log文件中!

注意:fflush虽然参数是stdout,但由于他只认识文件描述符,而此时1的内容已经被覆盖了,因此每一次写入缓冲区后立马刷新到文件里,实现同步过程,而不是等到缓冲区满了或者程序正常退出时才一次性刷新到文件里。

image-20230921121517002

若调用成功,会返回new description,也就是后者原本占有的文件描述符,失败则返回-1。

myshell添加重定向功能

我们在Linux进程控制一章中,自模拟实现过一个简易的命令行解释器myshell,下面我们可以像bash命令行一样实现重定向 > < 以及 >> 的功能!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <assert.h>#define NUM 1024
#define SIZE 32
#define SEP " "char *g_argv[SIZE];
char sub[SIZE];
char cmd_line[NUM];#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3int redir_status = NONE_REDIR;
//新增重定向功能,这里进行命令行解析
char *Checkredir(char *start)
{// ls -a>log.txtassert(start);char *end = start + strlen(start) - 1;while (end >= start){if (*end == '>'){// ls -a>>log.txtif (*(end - 1) == '>'){*(end - 1) = '\0';++end;redir_status = APPEND_REDIR;break;}// ls -a>log.txt*end = '\0';++end;redir_status = OUTPUT_REDIR;break;}else if (*end == '<'){// cat<log.txtredir_status = INPUT_REDIR;*end = '\0';++end;break;}else--end;}if (end >= start){return end;}elsereturn NULL;
}int main()
{while (1){printf("[root@localhost myshell]# ");fflush(stdout);memset(cmd_line, '\0', sizeof cmd_line);if (fgets(cmd_line, sizeof cmd_line, stdin) == NULL)continue;// ls -l -a\n\0 去除\ncmd_line[strlen(cmd_line) - 1] = '\0';char *ret = Checkredir(cmd_line);// 命令行解析: "ls -l -a\0\0" -> "ls" "-l" "-a"// strtok能做到此功能// 将解析出来的命令以及参数一个一个的放进指针数组中while (g_argv[index++] = strtok(NULL, SEP)); // 放入NULL表示还要继续解析上一个解析的字符串// 开始让子进程用进程替换执行用户输入的命令:pid_t id = fork();//先判断是否有重定向if (id == 0){if (ret){// 不为空说明是需要重定向int fd = -1;switch (redir_status){case INPUT_REDIR:fd = open(ret, O_RDONLY);assert(fd != -1);dup2(fd, 0);break;case OUTPUT_REDIR:fd = open(ret, O_WRONLY | O_TRUNC | O_CREAT, 0666);assert(fd != -1);dup2(fd, 1);break;case APPEND_REDIR:fd = open(ret, O_WRONLY | O_APPEND | O_CREAT, 0666);assert(fd != -1);dup2(fd, 1);break;default:perror("redirect error");break;}}// childprintf("以下功能由子进程进行进程替换所实现!\n");// 由于需要调系统程序,因此需要自动搜索环境变量PATHexecvp(g_argv[0], g_argv);exit(1);// 执行失败就退出,不会到下面去,和父进程互不干扰}// father// 父进程进行阻塞等待:int status = 0;pid_t res = waitpid(id, &status, 0);if (res > 0){printf("wait successfully!! exit code:%d\n", WEXITSTATUS(status));}}return 0;
}

效果展示:

test_red1

stdout和stderr

在Linux基础IO—上中我们学习了默认打开的三个流,分别是stdout,stderr和stdin,这三个流本质上是文件指针,指向的文件分别是外设:显示器与键盘,其中stdout和stderr都是显示器,stdin是键盘,这也就说明,显示器这个文件是被打开了两次的,因为有两个文件指针都指向了他,他们都能向显示器文件做对应的io操作,那他们用法上有什么区别呢?

我们来看代码:

//test: stdout stderr
int main()
{//stdout->1printf("hello printf 1\n");fprintf(stdout,"hello fprintf 1\n");//stderr->2perror("hello printf 1\n");//write->1const char*s1="hello write 1\n";write(1,s1,strlen(s1));//write->2const char*s2="hello write 2\n";write(2,s2,strlen(s2));//cout->1std::cout<<"hello cout 1"<<std::endl;//cerr->2std::cerr<<"hello cerr 2"<<std::endl;return 0;
}

编译运行后输出:

image-20230925104102167

不管是1还是2都能在我们的显示器看到,也证明了stdout和stderr对应的都是显示器

下面我们对程序进行重定向,看看会有什么变化:

image-20230925104502898

重定向后我们发现2号文件仍然向显示器打印了,而1号文件就正常的重定向进了文件里

结论:重定向默认是对1号文件描述符进行重定向

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

也就是1号和2号分别打开的显示器是不会影响彼此的,其实就是显示器文件是被打开了两次!

若就想让1号和2号的都往同一个文件重定向:

image-20230925105711518

这样就成功让1号和2号都重定向进了文件

如何使用?

一般而言如果程序运行有可能有问题的话,建议使用strerr或者cerr来打印,如果是常规的内容文本,建议使用stdout,cout来打印。

为什么?

因为在库中,有这样一个函数:image-20230925110612471

errno,被称为错误码,当某个函数调用时失败或者程序出现一些问题时,错误码就会被设置,而使用对应的perror就能向2号stderr中输出被设置的错误码所对应的错误信息,若你不想他直接运行后会在显示器上打印出来,就可以指定2号并重定向进日志文件中,方便管理者查看错误信息。image-20230925110751151

而perror的底层正是调用了strerror函数,这个函数在上篇有介绍过,不再细说。

void my_perror(const char* msg)
{fprintf(stderr,"%s",msg,strerror(errno));
}

FILE

我们在Linux基础io—上提到过,C语言所提供的文件操作函数,类似于fopen之类的函数,他们对文件的操作是需要用到文件指针FILE的,而我们现在已经学习了系统调用接口是通过文件描述符实现进程与打开文件建立联系的,而FILE文件指针底层正是封装了文件描述符才得以使用,本质上都是对系统接口进行二次封装提高可用性

Linux的设计哲学正是一切皆文件,体现在操作系统的设计软件层面,但其实Linux的底层也是C语言写的。

而我们学习过,文件正是=文件内容+文件属性,想用C语言实现这种具有面向对象思想的模块,只有一个东西能实现,那就是结构体struct。

而面向对象思想中,除了封装有有成员的属性,还有成员方法,但结构体如何实现成员方法呢?可以用函数指针来建立成员方法与成员的联系。

现在我们可以来看一下我们一直所说的文件指针FILE*,其指向的FILE底层究竟是如何实现的:

image-20230921162643605

我们查文档后发现,FILE其实是结构体struct _IO_FILE的别名:

image-20230921162845534

再转到_IO_FILE文件下,我们发现了有一个名为 _fileno的整型,这个整型其实就是我们熟悉的文件描述符fd,而不仅包含了fd,它还包含了该文件fd对应的语言层面的缓冲区结构!!

既然这样,由于底层各式各样的硬件,他们对应的操作方式都不同,但是他们的核心方式都是系统接口,也就是外设想要访问系统亦或是系统想要访问外设,在底层都是通过read,write,open,close来完成的,而这些硬件又被描述组织成文件被封装进了FILE中,这样一来,系统看待文件的方式都统一成为了管理结构体

缓冲区

什么是缓冲区

缓冲区其实就是用户自己提供的一块内存空间——char buffer[SIZE]

他的作用就是用来提高整机效率,提高用户的响应速度

我们知道文件是存在磁盘上的,若每次我们进行文件操作时都要直接往磁盘里加载,他的速度是非常慢的且效率不高,因此我们通过内存来过度,等到必要的时候再统一写入磁盘,这样减少频繁的访问磁盘,明显加速系统速度,这种模式叫做写回模式(write-back)

那缓冲区写入磁盘的时机又是什么时候呢?也就是缓冲区的刷新策略是什么?

缓冲区刷新策略:

  • 立即刷新
  • 行刷新(行缓冲)-‘\n’
  • 满刷新(全缓冲)

特殊情况:

  • 用户强制刷新(fflush)
  • 进程退出

我们来看一个简单的例子:

int main()
{ printf("hello Linux!");sleep(5); return 0;
}

按照代码逻辑来看,执行情况应该是先打印出来,再停留五秒后退出

但实际执行并不是这样,而是停留了五秒后再打印出来然后退出

其实这就是C语言库提供的缓冲区,若没有达到缓冲区的刷新要求,字符串会暂时保存起来,最后进程要退出了才会达到要求然后刷出来,若需要先刷出来再停留五秒的效果,则需要加上‘\n’告诉缓冲区需要刷新了或者fflush强制刷新

了解完基本概念,我们来看一段奇怪的代码:

// test:buffer:缓冲区
int main()
{const char *msg0 = "hello printf\n";const char *msg1 = "hello fwrite\n";const char *msg2 = "hello write\n";printf("%s", msg0);fwrite(msg1, strlen(msg0), 1, stdout);write(1, msg2, strlen(msg2));fork();return 0;
}

运行结果为:

image-20230921172237920

正常按照顺序打印一次,没毛病,符合预期。但我们稍微改动一下,我们运行程序后,将其重定向到文件中:

image-20230921173110863

这就奇了怪了,同一份代码,只是改变了输出方向,怎么就有不同的结果呢?

我们还观察到,fwrite和printf还出现了两次,而write只出现了一次,怎么就这么巧了?

初步推断:

出现两次可以初步推断这个结果跟程序最后的fork()有关,而write系统调用接口只有一次,其他接口都是两次可以推断罪魁祸首并不会影响系统接口!!!

两个问题:

  1. 为什么会打印两次?
  2. 为什么write系统接口只打印了一次?

为什么会打印两次?

我们注意到,我们在打印时,是已经使用了缓冲区的刷新策略—行刷新策略,那么当执行到fork的时候,这时候打印函数已经执行完毕了,按照正常来说,已经是将数据从缓冲区刷出来的,但实际上确并没有刷新!也就是说,执行到fork函数的时候,数据仍然存在C语言库管理的缓冲区中,并且这部分数据是属于父进程的数据。

  • 那么为什么会没有刷新呢??这不已经达到刷新策略的要求了吗?

我们需要注意到,我们这次运行时作了不同的动作,也就是进行了输出重定向!

而我们的输出重定向,本质就是将向显示器上打印的东西,转而写入了存储在磁盘上的文件!这时候隐形中的缓冲区刷新策略就改变了,变成了全缓冲,那么对应的行刷新也就没有作用了!

让我们接着来解释为什么会打印两次的问题:

存在缓冲区中的父进程的数据,在执行到fork()函数时,子进程采用写时拷贝的方法来保持进程间的独立性,父子进程共享一份代码和数据,但一旦父子进程的代码和数据需要被修改时,子进程会在物理内存中拷贝一份父进程的数据,并修改自己所持有的数据。真相渐渐水落石出了:

  • 父进程刷新缓冲区,本质上是把数据写入系统的过程,也就是写的过程。会发生写时拷贝

  • 父进程在刷新缓冲区前,由于子进程的存在,为了不让父进程的刷新影响到子进程,子进程也要拷贝一份一模一样的父进程缓冲区中的数据,因此有两份数据出现了,若提前强制刷新,缓冲区没数据了,子进程自然也就拷贝不到了

为什么write系统接口只打印了一次?

  • 因为我们所谈的缓冲区,是C标准库维给我们提供的用户及缓冲区,是属于语言层面的,跟系统层面的不是一个东西,当然不会影响系统接口!但是write在系统内核中有内核对应的缓冲区,也并非直接写到磁盘上的!

那么我们所说的用户级缓冲区他又在哪里呢??

  • 在我们前面提到过的FILE结构体,也就是_IO_FILE中,其内部不仅封装了文件描述符,还封装了缓冲区的结构!

我们调用fflush接口时,我们只需要传文件指针,那么他又是怎么知道缓冲区在哪里的,要去哪里刷新呢??

  • *fflush中传的参数是FILE ,FILE 指向的FILE结构体内封装有缓冲区结构!

缓冲区总结

  • 一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。

  • printf fwrite 库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。

  • 而我们放在缓冲区中的数据,就不会被立即刷新,甚至fork之后

  • 但是进程退出之后,会统一刷新,写入文件当中。

  • 但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的一份数据,随即产生两份数据。

  • write 没有变化,说明没有所谓的缓冲

综上:printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不再我们讨论范围之内。那这个缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统调用的“封装”,但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是C,所以由C标准库提供。

模拟实现缓冲区

关于我们对缓冲区的现理解,我们简单的模拟一下打开文件后对应分配到的缓冲区的结构,增加理解:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>typedef struct MYFILE_ MYFILE;struct MYFILE_
{int _fd;char buffer[1024];int end; // 缓冲区结尾
};MYFILE *fopen_(const char *pathname, const char *mode)
{assert(pathname && mode);MYFILE *fp = NULL;if (strcmp(mode, "r") == 0){}else if (strcmp(mode, "r+") == 0){}else if (strcmp(mode, "w") == 0){int fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd >= 0){//创建文件时会分配缓冲区fp = (MYFILE *)malloc(sizeof(MYFILE));memset(fp, 0, sizeof(MYFILE));fp->_fd = fd;}}else if (strcmp(mode, "w+") == 0){}else if (strcmp(mode, "a") == 0){}else if (strcmp(mode, "a+") == 0){}else{// nothing}
}
void fputs_(const char *message, MYFILE *fp)
{assert(message);assert(fp);// hellofputs\0// fputs\0strcpy(fp->buffer + fp->end, message);fp->end += strlen(message);// 刷新策略if (fp->_fd == 0){// stdin}else if (fp->_fd == 1){// stdoutif (fp->buffer[fp->end - 1] == '\n'){ write(fp->_fd, fp->buffer, fp->end);fp->end = 0;}}else if (fp->_fd == 2){// stderror}
}
void fflush_(MYFILE *fp)
{assert(fp);if(fp->end!=0){write(fp->_fd,fp->buffer,fp->end);//向磁盘中写入syncfs(fp->_fd);fp->end=0;}
}void fclose_(MYFILE *fp)
{assert(fp);fflush_(fp);close(fp->_fd);free(fp);
}int main()
{// close(1):debugMYFILE *fp = fopen_("test.txt", "w");assert(fp);fputs_("one\n", fp);sleep(1);fputs_("two", fp);sleep(1);fputs_("three", fp);sleep(1);fclose_(fp);return 0;
}

syncfs函数能够帮助我们将文件磁盘中正式写入

image-20230924211336264

只是简单模拟这个思路,底层真正想做到没有这么简单只为加深理解

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

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

相关文章

探索设计模式的魅力:代理模式揭秘-软件世界的“幕后黑手”

设计模式专栏&#xff1a;http://t.csdnimg.cn/U54zu 目录 引言 一、魔法世界 1.1 定义与核心思想 1.2 静态代理 1.3 动态代理 1.4 虚拟代理 1.5 代理模式结构图 1.6 实例展示如何工作&#xff08;场景案例&#xff09; 不使用模式实现 有何问题 使用模式重构示例 二、…

车载电子电器架构 —— 电子电气系统车载功能子系统

车载电子电器架构 —— 电子电气系统车载功能子系统 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 本就是小人物&#xff0c;输了就是输了&#xff0c…

osg模型的平移、缩放、旋转

加载2个模型&#xff0c;其中一个向上移动28个单位&#xff1b; 加载2个模型&#xff0c;其中一个缩放0.5倍&#xff0c;向下移动22个单位&#xff1b; 加载2个模型&#xff0c;其中一个缩放0.5倍、旋转45度、向右向下移动几个单位&#xff1b; 都是用矩阵实现的&#xff1b; …

【笔记】Harmony学习:下载安装 DevEco Studio 开发工具IDE

IDE 安装 从官网下载DevEco Studio 安装包后进行安装&#xff0c; 安装完毕后&#xff0c;本地环境可能要配置相关工具&#xff0c;可以通过下面的诊断检测一下本地环境&#xff0c;通过蓝色“Set it up now” 可以快速安装。 1. Node.js (for ohpm) 2. ohpm 下载op的包管理&a…

Codeforces Round 923 (Div. 3) C. Choose the Different Ones(Java)

比赛链接&#xff1a;Round 923 (Div. 3) C题传送门&#xff1a;C. Choose the Different Ones! 题目&#xff1a; ** Example** ** input** 6 6 5 6 2 3 8 5 6 5 1 3 4 10 5 6 5 6 2 3 4 5 6 5 1 3 8 10 3 3 3 4 1 3 5 2 4 6 2 5 4 1 4 7 3 4 4 2 1 4 2 2 6 4 4 2 1 5 2 3 …

实现远程开机(电脑)的各种方法总结

一.为什么要远程开机 因为工作需要&#xff0c;总是需要打开某台不在身边的电脑&#xff0c;相信很多值友也遇到过相同的问题&#xff0c;出门在外&#xff0c;或者在公司&#xff0c;突然需要的一个文件存在家里的电脑上&#xff0c;如果家里有人可以打个电话回家&#xff0c…

1978-2023年全国国内生产总值、分产业分行业增加值相关指标数据

1978-2023年全国国内生产总值、分产业分行业增加值相关指标数据 1、时间&#xff1a;1978-2023年 2、指标&#xff1a;国内生产总值(亿元)、第一产业增加值(亿元)、第二产业增加值(亿元)、第三产业增加值(亿元)、人均国内生产总值(元)、国民总收入指数(上年100)、国内生产总值…

SQL 表信息 | 统计 | 脚本

介绍 统计多个 SQL Server 实例上多个数据库的表大小、最后修改时间和行数&#xff0c;可以使用以下的 SQL 查询来获取这些信息。 脚本 示例脚本&#xff1a; DECLARE Query NVARCHAR(MAX)-- 创建一个临时表用于存储结果 CREATE TABLE #TableSizes (DatabaseName NVARCHAR…

2024Node.js零基础教程(小白友好型),nodejs新手到高手,(六)NodeJS入门——http模块

047_http模块_获取请求行和请求头 hello&#xff0c;大家好&#xff0c;那第二节我们来介绍一下如何在这个服务当中来提取 HTT 请求报文的相关内容。首先先说一下关于报文的提取的方法&#xff0c;我在这个文档当中都已经记录好了&#xff0c;方便大家后续做一个快速的查阅。 …

Shell脚本编程

文章目录 一、简介二、变量变量命名使用变量只读变量删除变量变量种类 三、数组四、算数运算五、条件测试数值测试字符串测试文件测试组合测试 六、选择执行七、用户交互read命令 八、循环语句for循环while循环until循环 九、函数十、调试脚本十一、环境配置bash配置文件案例&a…

Matlab使用点云工具箱进行点云配准ICP\NDT\CPD

一、代码 主代码main.m&#xff0c;三种配准方法任选其一 % 读取点云文件 source_pc pcread(bun_zipper.ply); target_pc pcread(bun_zipper2.ply);% 下采样 ptCloudA point_downsample(source_pc); ptCloudB point_downsample(target_pc);% 配准参数设置 opt param_set…

基于YOLOv8的暗光低光环境下(ExDark数据集)检测,加入多种优化方式---自研CPMS注意力,效果优于CBAM ,助力自动驾驶(二)

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文主要内容:详细介绍了暗光低光数据集检测整个过程&#xff0c;从数据集到训练模型到结果可视化分析&#xff0c;以及如何优化提升检测性能。 &#x1f4a1;&#x1f4a1;&#x1f4a1;加入 自研CPMS注意力 mAP0.5由原始的0.682提升…

大型语言模型(LLM)的优势、劣势和风险

最近关于大型语言模型的奇迹&#xff08;&#xff09;已经说了很多LLMs。这些荣誉大多是当之无愧的。让 ChatGPT 描述广义相对论&#xff0c;你会得到一个非常好&#xff08;且准确&#xff09;的答案。然而&#xff0c;归根结底&#xff0c;ChatGPT 仍然是一个盲目执行其指令集…

使用UMAP降维可视化RAG嵌入

大型语言模型&#xff08;LLMs&#xff09;如 GPT-4 已经展示了出色的文本理解和生成能力。但它们在处理领域特定信息方面面临挑战&#xff0c;比如当查询超出训练数据范围时&#xff0c;它们会产生错误的答案。LLMs 的推理过程也缺乏透明度&#xff0c;使用户难以理解达成结论…

【Linux】make和Makefile

目录 make和Makefile make和Makefile 我们使用vim编辑器的时候&#xff0c;在一个文件里写完代码要进行编译&#xff0c;要自己输入编译的指令。有没有一种可以进行自动化编译的方法——makefile文件&#xff0c;它可以指定具体的编译操作&#xff0c;写好makefile文件&#x…

新零售的升维体验,摸索华为云GaussDB如何实现数据赋能

新零售商业模式 商业模式通常是由客户价值、企业资源和能力、盈利方式三个方面构成。其最主要的用途是为实现客户价值最大化。 商业模式通过把能使企业运行的内外各要素整合起来&#xff0c;从而形成一个完整的、高效率的、具有独特核心竞争力的运行系统&#xff0c;并通过最…

【el-tree 文字过长处理方案】

文字过长处理方案 一、示例代码二、关键代码三、效果图 一、示例代码 <divstyle"height: 600px;overflow: auto"class"text item"><el-treeref"tree":data"treeData":props"defaultProps"class"filter-tree&…

fast.ai 深度学习笔记(四)

深度学习 2&#xff1a;第 2 部分第 8 课 原文&#xff1a;medium.com/hiromi_suenaga/deep-learning-2-part-2-lesson-8-5ae195c49493 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 来自 fast.ai 课程的个人笔记。随着我继续复习课程以“真正”理解它&#xff0c;这…

6.0 Zookeeper session 基本原理详解教程

客户端与服务端之间的连接是基于 TCP 长连接&#xff0c;client 端连接 server 端默认的 2181 端口&#xff0c;也就 是 session 会话。 从第一次连接建立开始&#xff0c;客户端开始会话的生命周期&#xff0c;客户端向服务端的ping包请求&#xff0c;每个会话都可以设置一个…

数据分析基础之《pandas(6)—高级处理》

一、缺失值处理 1、如何处理nan 两种思路&#xff1a; &#xff08;1&#xff09;如果样本量很大&#xff0c;可以删除含有缺失值的样本 &#xff08;2&#xff09;如果要珍惜每一个样本&#xff0c;可以替换/插补&#xff08;计算平均值或中位数&#xff09; 2、判断数据是否…