【Linux】文件IO深度解析:文件描述符与重定向的奥秘

🌈 个人主页:Zfox_
🔥 系列专栏:Linux

目录

  • 一:🔥 C语言中文件IO操作
    • 🥝 1.C语言中的开关读写文件
      • 🦋 1.1 fopen()
      • 🦋 1.2 fclose()
      • 🦋 1.3 fwrite()
      • 🦋 1.4 fread()
    • 🥝 2.stdin && stdout && stderr
    • 🥝 3.三个标准流和IO接口
  • 二:🔥 系统文件IO
    • 🥝 1.系统级别的开关读写文件
      • 🦋 1.1 open()
      • 🦋 1.2.close()
      • 🦋 1.3.write()
      • 🦋 1.4 read()
    • 🥝 2.系统文件IO VS C文件IO
  • 三:🔥 文件描述符fd
    • 🥝 3.1.什么是文件描述符
    • 🥝 3.2 如何创建文件描述符
    • 🥝 3.3 文件描述符的分配规则
  • 四:🔥 重定向
    • 🦋 dup2 系统调用
  • 五:🔥 FILE
    • 🦋 文件缓冲区
  • 六:🔥 共勉

一:🔥 C语言中文件IO操作

🥝 1.C语言中的开关读写文件

🦁 在学习 Linux 中的 IO 操作之前,我们先来简单的回顾一下,C语言中我们常用的一些 IO操作的接口。

🦋 1.1 fopen()

FILE* fopen(const char* path, const char* mode);

🍊 函数参数

  • path要打开的文件
  • mode:打开文件的方式
    • r:可读方式
    • r+:可读可写方式
    • w:可写方式,如果文件不存在,就创建一个文件。如果文件已经存在,就截断一个文件(清空文件内容)
    • w+:可读可写方式,如果文件不存在,就创建一个文件。如果文件已经存在,就截断一个文件(清空文件内容)
    • a:追加写,但是不可以读取内容。如果文件不存在,就创建一个文件。如果文件已经存在,就在文件的末尾开始追加写
    • a+:追加写,可以读取内容。如果文件不存在,就创建一个文件。如果文件已经存在,就在文件的末尾开始追加写

🍊 函数返回值

  • 成功:返回一个文件流指针FILE
  • 失败:返回NULL
  • 作用:以某种方式打开一个文件,并返回一个指向该文件的文件流指针。

🦋 1.2 fclose()

int fclose(FILE* fp);

作用:关闭传入的文件流指针指向的文件。

🦋 1.3 fwrite()

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

🍊 函数参数

  • ptr:写入文件的内容
  • size:往文件中写入的块的大小,单位为字节
  • count:预期写入的块数
  • stream:预期写入文件的文件指针

🍊 函数返回值

  • 成功:写入文件中的块数

常见用法

#include <cstdio>
#include <cstring>
#include <cstdlib>

int main()
{
    FILE* fp = fopen("myfile.txt", "w");
    if (!fp) {
        perror("fopen");
        exit(-1);
    }

    const char* msg = "hello Linux file\n";
    fwrite(msg, sizeof(char), strlen(msg), fp);

    fclose(fp);
    return 0;
}

🦋 1.4 fread()

size_t fread(void* ptr, size_t size, size_t count, 	FILE* stream);

🍊 函数参数

  • ptr:将从文件读取的内容保存在ptr所指向的空间中
  • size:定义读文件时块的大小,单位为字节
  • count:期望从文件中读的块数
  • stream:预期读取文件的文件指针

🍊 函数返回值

  • 成功从文件中读取的块的个数

常见用法

#include <cstdio>
#include <cstring>
#include <cstdlib>

int main()
{
    FILE* fp = fopen("myfile.txt", "r");
    if (!fp) {
        perror("fopen");
        exit(-1);
    }

    char buff[64];
    fread(buff, sizeof(char), sizeof(buff) / sizeof(char), fp);
    printf("%s", buff);

    fclose(fp);
    return 0;
}

🥝 2.stdin && stdout && stderr

🦁 默认情况下,C语言会自动打开两个输入输出流,分别是 stdinstdoutstderr

  • 这三个流的类型都是 FILE,也就是文件指针类型。

既然是文件指针,所以这三个指针分别指向键盘,显示器,显示器。后面的系统IO会再详细的讲解这三个输入输出流。

🥝 3.三个标准流和IO接口

💦 可以利用上面这三个标准流和C语言的IO接口,将字符串直接打印到显示器上。

#include <cstdio>
#include <cstring>
#include <cstdlib>

int main()
{
    FILE* fp = fopen("myfile.txt", "w+");
    if (!fp) {
        perror("fopen");
        exit(-1);
    }

    char buff[64];
    fread(buff, sizeof(char), 12, stdin);// 从键盘中输入到buff中

    fwrite(buff, sizeof(char), strlen(buff), stdout); // 从buff中写入到显示器上

    fclose(fp);
    return 0;
}

运行结果:
[lisi@hcss-ecs-a9ee work]$ ./a.out 
hello linux
hello linux                  

二:🔥 系统文件IO

  • 其实除了C语言之外,很多语言都是自己的IO接口函数,但是下面我们要谈论的就是 Linux 系统给我们提供的IO接口,也就是系统级别的IO接口。

🥝 1.系统级别的开关读写文件

🦋 1.1 open()

// 在打开的文件已经存在的时候
int open(const char* Path, int flags);
// 在打开的文件不存在的时候
int open(const char* Path, int flags, mode_t mode);

🍊 函数参数

  • Path:需要打开的文件
  • flags:打开文件的方式
    • 🎯 必选项
      • O_RDONLY:只读方式
      • O_WRONLY:只写方式
      • O_RDWR:读写方式
    • 🎯 可选项
      • O_TRUNC:截断文件(清空文件内容)
      • O_CREAT:文件不存在则创建文件
      • O_APPEND:追加方式

🍊 原理

  • 可以使用按位或的方式进行组合:如打开并创建只写文件 O_WRONLY | O_CREAT
  • 本质是利用了 位图 的方式来表示每一种的方式
    • mode :当打开一个新开的文件的时候,需要给一个文件设置权限,需要设置一个8进制的数字。这个和umask 也会有关系

🍊 函数返回值

  • 成功:返回一个文件描述符(后面介绍)
  • 失败:返回 -1
  • 作用:打开一个文件

🦋 1.2.close()

int close(int fd);

🍊 函数参数

  • fd:文件描述符
  • 作用:关闭一个文件

🦋 1.3.write()

ssize_t write(int fd, const void* buf, size_t count);

🍊 函数参数

  • fd:文件描述符
  • buf:将buf中的内容写到文件中
  • count:期望写入的字节数

🍊 返回值

  • 返回的字节数

代码示例:

#include <cstdio>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <cstring>
#include <cstdlib>

int main()
{
    // 创建一个权限为666的权限
    umask(0);
    int fd = open("file.txt", O_WRONLY | O_CREAT, 0666);
    if (fd < 0) {
        perror("open");
        exit(1);
    }
    // 将msg写入file.txt中
    const char* msg = "I am studing Linux IO\n";
    write(fd, msg, strlen(msg));
    close(fd);
    return 0;
}

🦋 1.4 read()

ssize_t read(int fd, void* buf, size_t count);

🍊 函数参数

  • fd:文件描述符
  • buf:将文件中的内容读到buf中
  • count:期望写入的字节数

🍊 返回值

  • 返回的字节数
#include <cstdio>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <cstring>
#include <cstdlib>

int main()
{
    int fd = open("file.txt", O_RDONLY);
    if (fd < 0) {
        perror("open");
        exit(1);
    }
    char buff[64];
    read(fd, buff, sizeof(buff));
    printf("%s", buff);
    close(fd);
    return 0;
}

🥝 2.系统文件IO VS C文件IO

  • 认识一下两个概念: 系统调用库函数

上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为 库函数(libc)。而, open close read write lseek 都属于系统提供的接口,称之为 系统调用接口

在这里插入图片描述
可以认为,f#系列的函数,都是对系统调用的封装,方便二次开发。

做一个小实验:

#include <stdio.h>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <string.h>    
#include <unistd.h>    
    
int main()    
{    
    int fd = open("log.txt",O_WRONLY | O_CREAT, 0666);    
    if(fd < 0)    
    {    
        perror("open fail");    
        return 1;    
    }    
    
    int cnt = 5;    
    char outBuffer[64];    
    while(cnt)    
    {    
        sprintf(outBuffer,"%s:%d\n","hello world",cnt--);    
        write(fd,outBuffer,strlen(outBuffer));                                                                                                                                             
    }    
    
    close(fd);    
    return 0;    
}    

特别注意:

🐮 我们在线文件中写入 string 的时候以、0 作为字符串的结尾,它是由 C语言规定的,但是这里是文件写入是,结束时是与 \0 无关的。所以在 strlen() 不需要 +1。

运行结果:

[lisi@hcss-ecs-a9ee work]$ cat log.txt 
hello world:5
hello world:4
hello world:3
hello world:2
hello world:1

🦁 通过上面测试我们不难发现C语言中的库函数接口是通过封装了系统调用接口实现的

在这里插入图片描述

三:🔥 文件描述符fd

  • 🐲 在上面open的接口中,我们提到了fd,这个也是open接口的返回值。而write和read接口也是通过fd这个参数使得文件可以读写,可以说fd是整个系统IO的灵魂,所以接下来,我们需要好好地理解一下fd

🥝 3.1.什么是文件描述符

🦁 在Linux下一切皆文件而大量的文件需要被高效的组织和管理,因此就诞生了文件描述符fd(file descriptor)。

🐮 文件描述符是内核为高效的管理已经被打开的文件所创建的索引,它是一个非负整数,用于指代被打开的文件,所有执行I/O操作的系统调用都是通过文件描述符完成的。

进程和文件之间的对应关系是如何建立的?
在这里插入图片描述

📖 由图可知文件描述符就是从0开始的正整数。但我们打开一个文件的时候,操作系统都需要创建一个数据结构来描述这个文件。所以 struct file 结构体就应运而生了,它就是表示打开的一个文件对象。

  • 当进程执行 open 函数的时候,必须要让进程和文件关联起来。所以在每一个进程的 PCB 中都是一个 struct files_struct* files 指针,它指向一张表 files_struct,这个表中有一个指针数组 fd_array[],其中指针数组的每一个元素都是一个指向 struct file 结构体的 struct file* 指针,而这个文件指针就指向打开的文件。

🦁 注意:向文件写入数据后,数据其实先写入对应文件的缓冲区当中,只有当将缓冲区中的内容刷新到磁盘当中时才算真正地写入到文件当中。

💦 小总结:

  • 所以本质上文件描述符就是 file_struct 结构体中 fd_array 数组的下标。而只要拿到了这个文件描述符,就可以找到对应的文件。

什么是进程创建会默认打开文件的 0,1,2 ?

  1. 在Linux中,进程是通过 进程描述符fd 来访问文件的,文件描述符实际上是一个整数。在程序刚启动的时候,默认有三个文件描述符,分别是:0(代表标准输入stdin)1(代表标准输出stdout)2(代表标准错误stderr)。对应的物理设备就是:键盘,显示器,显示器。

  2. 这三个文件设备都有自己对应的 struct file 系统会默认的生成这三个结构体,并使用双链表将他们连接起来,并且将 struct file 的地址放入到 struct file\* fd_array[] 数组的对应在 0, 1, 2 位置上。这个默认生成结构体并将地址放在 fd_array 数组的过程就叫做默认打开了标准输入流,标准输出流和标准错误流。

所以输入输出还可以采用如下方式:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

int main()
{
	char buf[1024];
	ssize_t s = read(0, buf, sizeof(buf));
	if(s > 0)
	{
		buf[s] = 0;
		write(1, buf, strlen(buf));
		write(2, buf, strlen(buf));
	}
	return 0;
}

补充:磁盘文件和内存文件的区别 ?

  1. 上面说的都是在操作进程打开的文件,正是因为操作系统中有大量的进程打开了大量的文件,所以需要使用 struct filestruct files_struct 这样的结构体去管理这些文件。而这些文件都是在内存中加载的文件,所以我们称之为 「内存文件」

  2. 如果一个文件储存在磁盘当中,我们就称之为 「磁盘文件」。这两种文件的关系就是当一个磁盘文件被加载到内存当中的话,就成为了内存文件。

  3. 磁盘文件由两部分构成:「文件内容」「文件属性」。文件内容就是文件中的数据内容,而文件属性(元信息)就是一个文件的基本信息。

🥝 3.2 如何创建文件描述符

进程获取文件描述符最常见的方式就是通过系统调用接口open或者是从父进程继承过来的。

虽然文件描述符对于每一个进程的PID都是唯一的,但是每一个进程都是一个进程描述表struct files_struct,用于管理进程描述符,当使用fork创建子进程的时候,子进程会获得父进程进程描述表的一个副本,所以子进程可以拿到父进程的进程描述符,因此就可以打开父进程所有的文件。

🥝 3.3 文件描述符的分配规则

🌿 我们先上结论, 文件描述符的分配规则:在 files_struct 数组当中,找到当前没有被使用的最小的一个下标,作为 新的文件描述符

如果再打开一个新的文件的话,就分配一个最小的没有使用的 文件描述符fd。因为默认打开了0, 1, 2,所以新的文件描述符就应该从3开始的。

#include <cstdio>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <cstring>
#include <cstdlib>

int main()
{
    close(0); // 关闭stdin
    int fd = open("file.txt", O_RDONLY);
    if (fd < 0) {
        perror("open");
        exit(1);
    }
    printf("%d\n", fd);
    close(fd);
    return 0;
}

运行结果:
[lisi@hcss-ecs-a9ee work]$ ./a.out 
0

四:🔥 重定向

那如果关闭1呢?看代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

int main()
{
	close(1);
	int fd = open("myfile", O_WRONLY | O_CREAT, 0644);
	if(fd < 0)
	{
		perror("open");
		return 1;
	}
	printf("fd: %d\n", fd);
	fflush(stdout);
	
	close(fd);
	exit(0);
}
  • 此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中,fd=1。这种现象叫做输出重定向。常见的重定向有:>, >>, <

那重定向的本质是什么呢 ?
在这里插入图片描述

每个文件描述符都是一个内核中文件描述信息数组的下标,对应有一个文件的描述信息用于操作文件,而重定向就是在不改变所操作的文件描述符的情况下,通过改变描述符对应的文件描述信息进而实现改变所操作的文件。

🦋 dup2 系统调用

Linux 系统中提供了系统接口dup2,这样函数是专门完成文件重定向的。

#include <unistd.h>

int dup2(int oldfd, int newfd);

🍊 函数参数

  • oldfd 为需要重定向的文件
  • newfd 为被重定向文件替换的文件

🍊 函数返回值

  • dup2 如果调用成功,返回 newfd ,否则返回-1。

🍊 注意事项:

  1. 如果 oldfd 不是有效的文件描述符,则调用 dup2 接口失败,并且此时的文件描述符 fd_array[newfd] 对应的文件没有关闭。

  2. 如果 oldfd 是有效的文件描述符,但是 oldfd == newfd,则该接口不会发生任何的操作,而是直接返回oldfd 文件描述符。

举例:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdlib>

int main()
{
    umask(0);
    int fd = open("file.txt", O_WRONLY | O_CREAT, 0666);
    if (fd < 0) {
        perror("open");
        exit(1);
    }
    close(1);
    dup2(fd, 1);
    printf("hello Linux IO\n");
    return 0;
}

运行结果:
[lisi@hcss-ecs-a9ee work]$ cat file.txt
hello Linux IO

🐮 使用printf打印的内容,打印到了file.txt文件当中。

五:🔥 FILE

🎯 因为库函数是对系统调用接口的封装,即fopen()中有open(),所以本质上来说访问文件都是通过文件描述符fd来访问的。所以我们可以在FILE结构体中有fd的存在。

为了搞清楚是不是这样的情况,我们可以通过源码来一探究竟。

我们可以在stdio.h的头文件下看到FILE。

typedef struct _IO_FILE FILE;
  • 也就是说 FILE 其实是 struct _IO_FILE 。我们也可以在 libio.h 头文件下找到 struct _IO_FILE 结构体的定义,其中有一个叫 _fileno 的成员,这个变量中其实就是文件描述符 fd
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 0
	int _blksize;
#else
	int _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
};

现在重新理解一下,fopen究竟在底层做什么?

  1. 给调用的用户申请struct FILE结构体变量,并返回地址(FILE*)。

  2. 在底层通过 open 打开文件,并返回 fd,把 fd 填充进 FILE 变量中的 _fileno。

综上:在C语言中,fread(), fwrite(), fputs(), fgets(),都是通过 FILE* 指针找到 FILE结构体,然后再在FILE 结构体中找到 _fileno 从而知道 fd,最后通过 fd 操作内存中的文件。

🦋 文件缓冲区

在这里插入图片描述

为什么我重定向以后它并没有直接给我写到指定文件中,而fflush(stdout)后就写进去了呢?

  • 首先我们要知道,在C语言中我们使用的printf、scanf、fprintf、fscanf、fwrite、fread等都要求有一个FILE*的指针。
  • 所以,在调用这些函数进行操作时,它并没有直接调用系统调用read、write直接拷贝到文件的内核缓冲区,因为频繁的调用系统调用的成本太高了,效率低。

🍊 所以怎么能提高效率呢?

  • 通过用户级缓冲区!! 你printf、fprintf等只需要将内容拷贝到用户级缓冲区中任务就完成了,无非就是在拷贝的过程中进行一下格式化;等用户级缓冲区攒了足够多的数量,在统一调用系统调用写入到文件的内核缓冲区,提高了效率。

  • 该缓冲区在FILE结构体中,刷新的本质就是从用户级缓冲区拷贝到内核的文件缓冲区。

🍊 用户级缓冲区有以下几种刷新方案:

  • 显示器文件:行刷新
  • 普通文件:缓冲区写满再刷新
  • 不缓冲(语言级无需刷新)

我们将最开始的代码修改一下会发现,如果我不调用任何的close或者调用fclose,内容可以正常打印出来,这是为什么呢?

在这里插入图片描述

因为,当一个进程退出的时候,会自动刷新自己的缓冲区(所有的FILE对象内部,包括stdin、stdout、stderr);fclose是C语言级的,调用它关闭FILE时,也会自动刷新。

那close(fd)后,为什么不会刷新呢?
在这里插入图片描述

此时尽管“表面上”是向显示器中打,应该是行刷新,那么我不自己刷新应该也可以显示出来呀? - - 此时不是行刷新,因为显示器文件早就关闭了,1中放的是普通文件,应执行写满刷新的策略。

🌿 那操作系统是什么时候将文件内核缓冲区的内容刷新到外设中的呢?我能不能控制呢?

  • 可以通过系统调用fsync可以做到
    在这里插入图片描述

一个简单的题目:
在这里插入图片描述

如果在调用函数时不加 \n,即使不重定向,也是上图所示的打印效果。

六:🔥 共勉

以上就是我对 【Linux】IO深度解析:文件描述符与重定向的奥秘 的理解,觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉
在这里插入图片描述

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

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

相关文章

内容安全与系统构建加速,助力解决生成式AI时代的双重挑战

内容安全与系统构建加速&#xff0c;助力解决生成式AI时代的双重挑战 0. 前言1. PRCV 20241.1 大会简介1.2 生成式 Al 时代的内容安全与系统构建加速 2. 生成式 AI2.1 生成模型2.2 生成模型与判别模型的区别2.3 生成模型的发展 3. GAI 内容安全3.1 GAI 时代内容安全挑战3.2 图像…

面试宝典(五):用三个线程按顺序循环打印123三个数字,比如123123123

要使用三个线程按顺序循环打印123三个数字&#xff0c;势必要控制线程的执行顺序&#xff0c;可以使用java.util.concurrent包中的Semaphore类来控制线程的执行顺序。 代码示例 import java.util.concurrent.Semaphore;public class SequentialPrinting123 {private static Se…

第T8周:猫狗识别

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营](https://mp.weixin.qq.com/s/0dvHCaOoFnW8SCp3JpzKxg) 中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊](https://mtyjkh.blog.csdn.net/)** &#x1f37a; 要求&#xff1a; 了解mode…

离线电脑 Visual Studio Community 2017:您的许可证已过期

VS 2017社区版&#xff0c;打开后提示&#xff1a; “您的许可证已过期&#xff0c;必须进行更新。请确保已连接Internet&#xff0c;然后检查更新的许可证以继续使用本产品” 解决办法&#xff1a; &#xff08;1&#xff09;在另一台可以联网的电脑上&#xff0c;更新VS20…

8.Linux按键驱动-中断下半部

1.编程思路 1.1在gpio结构体中添加tasklet_struct结构体 1.2在probe函数中初始化tasklet结构体 1.3在中断服务程序中调度tasklet 1.4在这个函数中执行其它任务 2.代码&#xff1a; 应用程序和Makefile和上节一致 https://blog.csdn.net/weixin_40933496/article/details/1…

通过call指令来学习指令摘要表的细节

E8 cw cw 表示E8后面跟随2 字节 (什么数不知道) rel16 指在与指令同一代码段内的相对地址偏移 D ,指向Instruction Operand Encoding 表中的D列, 他告诉我们 操作数1 是一个0FFSET N.S. 在64位模式下&#xff0c;某些指令需要使用“地址覆盖前缀”&#xff08;address over…

RL学习笔记-马尔可夫过程

参考资料&#xff1a;蘑菇书、周博磊老师课程 在强化学习中&#xff0c;智能体与环境交互是通过马尔可夫决策过程来表示的&#xff0c;因此马尔可夫决策过程是强化学习的基本框架。 马尔可夫性质 指一个随机过程在给定现在状态及所有过去状态情况下&#xff0c;其未来状态的条件…

Golang | Leetcode Golang题解之第506题相对名次

题目&#xff1a; 题解&#xff1a; var desc [3]string{"Gold Medal", "Silver Medal", "Bronze Medal"}func findRelativeRanks(score []int) []string {n : len(score)type pair struct{ score, idx int }arr : make([]pair, n)for i, s : …

BERT语言模型详解【Encoder-Only】

NLP-大语言模型学习系列目录 一、注意力机制基础——RNN,Seq2Seq等基础知识 二、注意力机制【Self-Attention,自注意力模型】 三、Transformer图文详解【Attention is all you need】 四、大语言模型的Scaling Law【Power Low】 五、大语言模型微调方法详解【全量微调、PEFT、…

Android Studio 导入/删除/新建库的模块(第三方项目) - Module

文章目录 一、导入module项目 Module空项目如何导入Project工程项目二、删除module项目三、新建module项目(不常用) 一、导入module项目 首先&#xff0c;你必须要有一个工程(Project),才可以打开项目(Module) 第一步骤&#xff1a;右键项目依次点击 New -> Module 1、工…

LLM | 论文精读 | 基于大型语言模型的自主代理综述

论文标题&#xff1a;A Survey on Large Language Model based Autonomous Agents 作者&#xff1a;Lei Wang, Chen Ma, Xueyang Feng, 等 期刊&#xff1a;Frontiers of Computer Science, 2024 DOI&#xff1a;10.1007/s11704-024-40231-1 一、引言 自主代理&#xff08;…

AI 提示词(Prompt)入门 :ChatGPT 4.0 高级功能指南

这段时间 GPT4 多了很多功能&#xff0c;今天主要是增加了 GPTs Store 的介绍和 创建 GPTs 的简单方法&#xff0c;那么我们开始吧&#xff0c;文末有彩蛋。 这里主要讲解如下几个点&#xff1a; 1&#xff1a; ChatGPT 4.0 插件的使用 2&#xff1a;ChatGPT 4.0 高级数据分…

【已解决】【hadoop】【hive】启动不成功 报错 无法与MySQL服务器建立连接 Hive连接到MetaStore失败 无法进入交互式执行环境

启动hive显示什么才是成功 当你成功启动Hive时&#xff0c;通常会看到一系列的日志信息输出到控制台&#xff0c;这些信息包括了Hive服务初始化的过程以及它与Metastore服务连接的情况等。一旦Hive完成启动并准备就绪&#xff0c;你将看到提示符&#xff08;如 hive> &#…

大数据Azkaban(二):Azkaban简单介绍

文章目录 Azkaban简单介绍 一、Azkaban特点 二、Azkaban组成结构 三、Azkaban部署模式 1、solo-server ode&#xff08;独立服务器模式&#xff09; 2、two server mode&#xff08;双服务器模式&#xff09; 3、distributed multiple-executor mode&#xff08;分布式多…

FPGA第 13 篇,使用 Xilinx Vivado 创建项目,点亮 LED 灯,Vivado 的基本使用(点亮ZYNQ-7010开发板的LED灯)

前言 在FPGA设计中&#xff0c;Xilinx Vivado软件是一款功能强大的设计工具&#xff0c;它不仅支持硬件描述语言&#xff08;HDL&#xff09;的开发&#xff0c;还提供了丰富的图形化设计界面&#xff0c;方便用户进行硬件设计、调试和测试。这里我们将详细介绍&#xff0c;如…

RabbitMQ 高级特性——事务

文章目录 前言事务配置事务管理器加上Transactional注解 前言 前面我们学习了 RabbitMQ 的延迟队列&#xff0c;通过延迟队列可以实现生产者生产的消息不是立即被消费者消费。那么这篇文章我们将来学习 RabbitMQ 的事务。 事务 RabbitMQ 是基于 AMQP 协议实现的&#xff0c;…

Gstreamer的webrtcbin插件

1、输入参数 static GOptionEntry entries[] {{"peer-id", 0, 0, G_OPTION_ARG_STRING, &peer_id, "String ID of the peer to connect to", "ID"},{"server", 0, 0, G_OPTION_ARG_STRING, &server_url, "Signalling se…

unity项目导出安卓工程后,在AndroidStudio打包报错:unityLibrary:BuildIl2CppTask‘.

下面这个是我在unity开发者社区提问后&#xff0c;他们回答得&#xff1a; 解决方案&#xff1a;我这边按照这几个方案检查了下&#xff0c;NDK和JDK都没问题&#xff0c;最后重启电脑才解决的&#xff0c;应该是文件被锁定了&#xff0c;我用的windows系统的。 验证&#xff…

一篇文章快速认识YOLO11 | 旋转目标检测 | 原理分析 | 模型训练 | 模型推理

本文分享YOLO11的旋转目标检测任务&#xff0c;在原始目标检测中&#xff0c;添加多一个角度预测&#xff0c;实现定向边界框检测。 其中旋转角度算法设计中&#xff0c;通过回归预测实现的。 目录 1、旋转目标检测概述 2、YOLO11中的OBB数据格式 3、分析模型配置参数 4、…

dmsql日志分析工具部署与使用DM8/DM7

dmsql日志分析工具部署与使用DM8/DM7 1 环境介绍2 JAVA 环境变量配置2.1 Os Kylin 10 JAVA 环境变量配置2.2 Windos7 JAVA环境变量配置 3 数据库配置3.1 数据库初始化参数3.2 数据库创建表 4 配置DMLOG日志分析工具4.1 Kylin v10 配置DMLOG日志分析工具4.2 执行日志分析4.3 Win…