【Linux】文件描述符 | 重定向 | C文件指针与fd的关系 | 用户级缓冲区

文章目录

  • 一、文件描述符
    • 1. 理解:Linux下一切皆文件
    • 2. 文件描述符(fd)的概念
    • 3. 文件描述符的分配规则
    • 4. 进程创建时默认打开的 0 & 1 & 2 号文件
  • 二、重定向
    • 1. 重定向的本质
    • 2. 使用dup2系统调用函数
    • 3. bash下的三种重定向
    • 4. 三种重定向的实现
      • 输出重定向
      • 输入重定向
      • 追加重定向
  • 三、理解C语言文件指针和Linux中的文件fd的关系
  • 四、理解用户级缓冲区

[!Abstract] Linux文件相关重点

  • 复习C文件IO相关操作
  • 认识文件相关系统调用接口
  • 认识文件描述符,理解重定向
  • 对比fd和FILE,理解系统调用和库函数的关系
  • 理解文件系统中inode的概念
  • 认识软硬链接,对比区别
  • 认识动态静态库,学会结合gcc选项,制作动静态库

一、文件描述符

1. 理解:Linux下一切皆文件

在Linux系统中,"一切皆文件"是一个核心概念,意味着系统中的各种资源,包括设备、套接字、管道等,都被抽象为文件,并通过文件描述符进行访问。

[!Quote]
在 【Linux】从冯诺依曼体系结构到操作系统 中我们学到,操作系统是一款管理软件,它通过向下管理好各种软硬件资源 (手段),来向上提供良好 (安全、稳定、高效) 的运行环境 (目的);也就是说,键盘、显示器、磁盘、网卡等硬件也是由操作系统来管理的。而操作系统管理软硬件的方法是 先描述、再组织,即先将这些设备的各种属性抽象出来组成一个结构体,然后为每一个设备都创建一个结构体对象,再用某种数据结构将这些对象组织起来;这也就是我们上面学习到的 文件内核数据结构 file;

请添加图片描述

同时,每种硬件的访问方法都是不一样的,比如,向磁盘中读写数据与向网卡中读写数据是有明显差异的,所以操作系统需要为每一种硬件都单独提供对应的 Read、Write 方法,这些方法位于驱动层。

但是,内核数据结构是位于操作系统层的,它如何与对应的读写方法联系起来呢?-- 通过函数指针,即在 struct file 结构体中创建函数指针变量,用于指向具体的 Write 和 Read 方法函数,这样每一个硬件都可以通过自己 file 对象中的 writep 和 readp 函数指针变量来找到位于驱动层的 Write 和 Read 方法,如下:


struct file 
{
//文件的各种属性
int types;  //文件的类型
int status;  //文件的状态
int (*writep)(...);  //函数指针,指向读函数
int (*readp)(...);	//函数指针,指向写函数
struct file* next;  //下一个file对象的地址
...
}

如图,站在操作系统内核数据结构上层来看,所有的软硬件设备和文件统一都是 file 对象,即 Linux 下一切皆文件。请添加图片描述

注:对于键盘来说,我们只能从其中读入数据,而并不能向其写入数据;同样的,对于显示器来说,我们只能向其写入数据,而并不能从它读入数据;所以,键盘的 Write 方法和显示器的显示器的 Read 方法我们都设为 NULL。

其实 Linux 一切皆文件的特性就是面向对象语言多态的特性,file 结构体相当于基类,驱动层的各种方法和结构就相当于子类。(Linux 在编写时C++等面向对象的语言还并没有出现,所以这里是用C语言模拟实现C++面向对象)

同时,struct file 是操作系统当中虚拟出来的一层文件对象,在 Linux 中,我们一般将这一层称为 虚拟文件系统 vfs,通过它,我们就可以摒弃掉底层设备的差别,统一使用文件接口的方式来进行文件操作。


原文链接:https://blog.csdn.net/m0_62391199/article/details/128600173

2. 文件描述符(fd)的概念

先说结论:文件描述符(file descriptor)本质上是文件描述符表的数组下标

文件是由进程运行时打开的,一个进程可以打开多个文件,而系统当中又存在大量进程,也就是说,在系统中任何时刻都可能存在大量已经打开的文件。因此,操作系统务必要对这些已经打开的文件进行管理,管理的方式还是:先描述在组织。

  • 操作系统会为每个已经打开的文件创建各自的 struct file 结构体 - 描述
  • 然后将这些结构体以双链表的形式连接起来 - 组织
  • 之后操作系统对文件的管理也就变成了对这张双链表的增删查改等操作。
    而为了区分已经打开的文件哪些属于特定的某一个进程,我们就还需要建立进程和文件之间的对应关系。

[!Question] 进程和文件之间的对应关系如何建立?

  • 我们知道,当一个程序运行起来时,操作系统会将该程序的代码和数据加载到内存,然后为其创建对应的task_structmm_struct、页表等相关的数据结构,并通过页表建立虚拟内存和物理内存之间的映射关系。
    ![[进程地址空间_页表_文件fd.drawio (1).png]]
  • task_struct当中有一个指针,该指针指向一个名为files_struct的结构体:
    请添加图片描述

在该结构体当中就有一个名为fd_array的指针数组,该数组的下标就是我们所谓的文件描述符

  • 下面是源码中fd_array的定义:
    请添加图片描述

  • fd_array的大小可能是32或64,取决于平台:
    请添加图片描述


当进程用open()系统调用打开log.txt文件时,我们需要先将该文件从磁盘当中加载到内存,形成对应的struct file,将该struct file连入文件双链表,并将该结构体的首地址填入fd_array数组当中下标为3的位置,使得fd_array数组中下标为3的指针指向该struct file,最后open()返回该文件的文件描述符给调用进程即可。

进程与被打开文件:
当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件,于是就有了 file 结构体,表示一个已经打开的文件对象。
而进程执行 open 系统调用,所以必须让进程和文件关联起来,于是每个进程都有一个 *files 指针,指向一张表 files_struct,该表最重要的部分就是包含一个指针数组,数组中每个元素都是一个指向打开文件的指针。
因此,一个进程只要拿着文件描述符,就可以找到此进程打开的文件

3. 文件描述符的分配规则

尝试打开几个文件来研究文件描述符的分配规则:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>// C语言中 # 在宏当中的作用:将参数插入到字符串中
#define FILE_NAME(number) "log.txt"#number int main() {umask(0);int fd1 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);int fd2 = open(FILE_NAME(2), O_WRONLY | O_CREAT | O_APPEND, 0666);int fd3 = open(FILE_NAME(3), O_WRONLY | O_CREAT | O_APPEND, 0666);int fd4 = open(FILE_NAME(4), O_WRONLY | O_CREAT | O_APPEND, 0666);int fd5 = open(FILE_NAME(5), O_WRONLY | O_CREAT | O_APPEND, 0666);printf("fd1:%d\n", fd1);printf("fd2:%d\n", fd2);printf("fd3:%d\n", fd3);printf("fd4:%d\n", fd4);printf("fd5:%d\n", fd5);close(fd1);close(fd2);close(fd3);close(fd4);close(fd5);
}

现象:
请添加图片描述

可以看到,文件描述符是连续分配且依次增大的,这很合理,因为文件描述符本质上是数组下标。但是这里有一个很奇怪的地方,就是文件描述符是从3开始的,那么0、1、2号下标呢?这是由于三个默认打开的标准流引起的,分别是标准输入、标准输出、标准错误流

4. 进程创建时默认打开的 0 & 1 & 2 号文件

[!Abstract]
在Linux下,进程启动时,会默认打开文件描述符为0(stdin)、1(stdout)、2(stderr)的文件。这三个文件描述符分别代表标准输入、标准输出和标准错误。默认情况下,

#include <stdio.h>int main() 
{printf("stdin->fd:%d\n", stdin->_fileno);printf("stdout->fd:%d\n", stdout->_fileno);printf("stderr->fd:%d\n", stderr->_fileno);
}

请添加图片描述

  1. stdin(标准输入):默认情况下,stdin对应键盘输入。当你在终端输入内容时,这些输入会被送到stdin。

  2. stdout(标准输出):默认情况下,stdout对应显示器(终端屏幕)。当程序输出信息时,这些信息会被显示在终端屏幕上。

  3. stderr(标准错误):同样,默认情况下,stderr也对应显示器(终端屏幕)。但stderr通常被用于输出错误信息,使得错误信息和正常输出信息可以分开显示。

在实际应用中,这些标准输入输出也可以被重定向到其他地方,例如从文件读取输入或将输出写入文件。

既然系统默认打开三个文件,那么我们可不可以将其关闭呢?当然可以!

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>#define FILE_NAME(number) "log.txt"#number int main() {close(0);close(2);umask(0);int fd1 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);int fd2 = open(FILE_NAME(2), O_WRONLY | O_CREAT | O_APPEND, 0666);int fd3 = open(FILE_NAME(3), O_WRONLY | O_CREAT | O_APPEND, 0666);int fd4 = open(FILE_NAME(4), O_WRONLY | O_CREAT | O_APPEND, 0666);int fd5 = open(FILE_NAME(5), O_WRONLY | O_CREAT | O_APPEND, 0666);printf("fd1:%d\n", fd1);printf("fd2:%d\n", fd2);printf("fd3:%d\n", fd3);printf("fd4:%d\n", fd4);printf("fd5:%d\n", fd5);close(fd1);close(fd2);close(fd3);close(fd4);close(fd5);
}

现象:当下标为0号和2号的文件描述符所指向的文件被关闭后,

close(0);
close(2);

系统将其分配给了新打开的文件 log.txt1 和 log.txt2。请添加图片描述

[!Attention] 注意
close 关闭文件并不是将 fd 指向的 file 对象释放掉,而仅仅是让当前进程文件描述符表中的对应下标不再指向该 file 对象,因为同一个文件可能会被多个进程访问,特别是父子进程。
事实上,Linux的文件对象底层采用 f_count 引用计数的方式来实现:

  • 每当一个新的文件描述符指向同一个文件对象时,该文件对象的引用计数会增加。
  • 当文件描述符被关闭时,引用计数会减少。
  • 当引用计数变为零时,表示没有文件描述符或进程再引用这个文件对象,此时文件对象可以被释放,可以真正意义上的关闭文件。

总结文件描述符的分配规则:从小到大依次搜寻,寻找未被使用的最小 fd 作为新打开文件的 fd。

二、重定向

1. 重定向的本质

重定向的本质就是修改文件描述符下标对应的 struct file* 指针的指向。

我们上面在演示 fd 分配规则的时候,关闭了标准输入和标准错误,那么如果我们关闭标准输出呢?如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>#define FILE_NAME "log.txt"int main() 
{close(1);  //关闭标准输出 int fd = open(FILE_NAME,  O_WRONLY | O_CREAT | O_TRUNC, 0666);if(fd == -1) {perror("open");return 1;}printf("fd:%d\n", fd);fprintf(stdout, "fd:%d\n", fd);fflush(stdout);close(fd);return 0;
}

现象:
请添加图片描述

请添加图片描述

本来应该往一个文件(屏幕)中写入数据,但是却写入到另一个文件(log.txt)中去了,这种特性就叫做重定向;而重定向的本质是上层使用的 fd 不变,在内核中更改 fd 指向的 file 对象,即更改文件描述符表数组中 fd 下标中的内容,让其变为另一个 file 对象的地址。

2. 使用dup2系统调用函数

我们可以使用上面 close(1) 的方式实现重定向,但是我们发现先关闭、再打开这种方式非常麻烦,并且如果 0 和 1 号 fd 都被关闭时,我们还需要先创建一个无用的临时文件占用掉 0 号 fd 之后才能使新文件的 fd 为 1。为了解决这种尴尬的情况,操作系统提供了一个系统调用接口 dup2 来让我们直接进行重定向。

dup2的函数原型如下:

int dup2(int oldfd, int newfd);

函数功能: dup2会将fd_array[oldfd]的指针拷贝到fd_array[newfd]当中,如果有必要的话我们需要先使用关闭文件描述符为newfd的文件。
函数返回值: dup2如果调用成功,返回newfd,否则返回-1。

使用dup2时,我们需要注意以下两点:

  1. 如果oldfd不是有效的文件描述符,则dup2调用失败,并且此时文件描述符为newfd的文件没有被关闭。
  2. 如果oldfd是一个有效的文件描述符,但是newfd和oldfd具有相同的值,则dup2不做任何操作,并返回newfd。

使用一下:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>#define FILE_NAME "log.txt"int main() {umask(0);int fd = open(FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666);if(fd == -1) {perror("open");return 1;}int ret = dup2(fd, 1);  //fd是oldfd,1是newfd,让1成为fd的拷贝if(ret == -1) {perror("dup2");return 1;}printf("fd:%d\n", fd);fprintf(stdout, "fd:%d\n", fd);fflush(stdout);close(fd);return 0;
}

请添加图片描述

3. bash下的三种重定向

在 Linux 命令行中,可以使用 < 进行输入重定向,> 进行输出重定向,>> 进行追加重定向。下面是简单的例子演示这三种重定向的用法:

  1. 输出重定向 >
# 将命令 ls 的输出写入文件 output.txt
ls -l > output.txt

>ls -l 命令的输出重定向到文件 output.txt
请添加图片描述

  1. 追加重定向 >>
# 将命令 echo 的输出追加到文件 append.txt
echo "New content" >> append.txt

>>echo "New content" 命令的输出追加到文件 append.txt
请添加图片描述

[!Question] 没有在这个字符串末尾添加‘\n’,但是却换了行???


在类Unix系统中,包括Linux,echo命令默认会在输出字符串的末尾自动添加换行符(\n)。所以,当你执行五次echo "New content" >> append.txt时,每次输出的字符串末尾都会有一个换行符。

可以通过查看文件的内容,或者使用cat -v命令来显示不可见字符,来确认是否有换行符。

cat -v append.txt

如果希望在每次追加时都不添加换行符,可以使用echo -n来禁止echo添加换行符:

echo -n "New content" >> append.txt

这样做会在文件中追加不带换行符的字符串。

  1. 输入重定向 <
# 从文件 input.txt 中读取内容,并传递给命令 cat
cat < input.txt

通过 < 进行输入重定向,你可以让 cat 命令读取文件 input.txt 的内容而不是从标准输入中读取。这样,cat 将会把 input.txt 文件的内容输出到终端,就好像你直接键入了这些内容一样。

上述命令的执行过程是,cat 命令从标准输入读取数据,但 < input.txt 的作用是将文件 input.txt 的内容传递给 cat 命令的标准输入。因此,cat 将显示 input.txt 文件的内容到终端上。

cat input.txtcat < input.txt 在输出上的结果可能是相同的,但它们的本质是有一些区别的。

  1. cat input.txt 这是直接使用文件名作为参数传递给 cat 命令。cat 命令会打开文件 input.txt 并显示其内容。这种方式直接在命令行中指定了文件名,cat 命令会自己打开文件并读取内容。
cat input.txt
  1. cat < input.txt 这是使用输入重定向符 < 将文件 input.txt 的内容传递给 cat 命令的标准输入。在这种情况下,cat 命令并不知道文件名,而是从标准输入中读取数据。这样的写法主要用于将文件的内容传递给需要从标准输入读取数据的命令。
cat < input.txt

从输出结果上来看,两者可能是相同的,因为它们都会将文件的内容输出到终端。但是,本质上,第一个例子是直接将文件名传递给 cat 命令,而第二个例子是通过输入重定向将文件的内容传递给 cat 命令。

4. 三种重定向的实现

输出重定向

通过 dup2(fd, 1) 系统调用将目标文件 fd 中的内容拷贝到 1 号 fd 中,从而将本该写入到显示器中的数据写入到目标文件中。注:stdout的值是1。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>#define FILE_NAME "log.txt"int main()
{umask(0);int fd = open(FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666);if(fd == -1){perror("open");return 1;}int ret = dup2(fd, 1);  //fd是oldfd,1是newfd,让1成为fd的拷贝if(ret == -1){perror("dup2");return 1;}printf("I am printf, fd:%d\n", fd);fprintf(stdout, "I am fprintf through stdout, fd:%d\n", fd);char* msg = "hello redirect\n";write(1, msg, strlen(msg));fflush(stdout);close(fd);return 0;
}

请添加图片描述

“hello redirect” 是最后写入的,但是它却出现在了文本的最前面,这还是由缓冲区造成的,我们后文会详细讲解缓冲区。

输入重定向

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>#define FILE_NAME "log.txt"int main() 
{int fd = open(FILE_NAME, O_RDONLY);if(fd == -1) {perror("open");return 1;}int ret = dup2(fd, 0);  //输入重定向if(ret == -1) {perror("dup2");return 1;}char buf[64];while(fgets(buf, sizeof(buf) - 1, stdin) != NULL) {buf[strlen(buf)] = '\0';printf("%s", buf);  //将从stdin中读入的数据(重定向后是从log.txt中读取)写入到stdout中}close(fd);return 0;
}

请添加图片描述

追加重定向

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>#define FILE_NAME "log.txt"int main() 
{umask(0);int fd = open(FILE_NAME, O_WRONLY | O_CREAT | O_APPEND, 0666);if(fd == -1) {perror("open");return 1;}int ret = dup2(fd, 1);  //fd是oldfd,1是newfd,让1成为fd的拷贝if(ret == -1) {perror("dup2");return 1;}printf("fd:%d\n", fd);fprintf(stdout, "fd:%d\n", fd);char* msg = "hello redirect\n";write(1, msg, strlen(msg));fflush(stdout);close(fd);return 0;
}

请添加图片描述

三、理解C语言文件指针和Linux中的文件fd的关系

  • 因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。
  • 所以C库当中的FILE结构体内部,必定封装了fd

验证一下:
首先,我们在/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
};

[!Question] 现在我们再来理解一下C语言当中的fopen函数究竟在做什么?


fopen函数在上层为用户申请FILE结构体变量,并返回该结构体的地址(FILE*),在底层通过系统接口open打开对应的文件,得到文件描述符fd,并把fd填充到FILE结构体当中的_fileno变量中,至此便完成了文件的打开操作。
而C语言当中的其他文件操作函数,比如fread、fwrite、fputs、fgets等,都是先根据我们传入的文件指针找到对应的FILE结构体,然后在FILE结构体当中找到文件描述符,最后通过文件描述符对文件进行的一系列操作。

四、理解用户级缓冲区

我们常说的缓冲区一般都是是语言级的,而不是操作系统级别的,操作系统的缓冲区叫内核缓冲区。

来段代码研究一下:

#include <stdio.h>
#include <string.h>
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(msg1), 1, stdout);write(1, msg2, strlen(msg2));fork(); // 注意fork的位置!return 0;
}

运行出结果:
请添加图片描述

但如果对进程实现输出重定向呢? ./test > file, 我们发现结果变成了:
请添加图片描述

我们发现printffwrite(库函数)都输出了2次,而write只输出了一次(系统调用)。为什么呢?肯定和fork()有关!

首先我们应该知道的是,缓冲的方式有以下三种:

  1. 无缓冲。
  2. 行缓冲。(常见的对显示器进行刷新数据)
  3. 全缓冲。(常见的对磁盘文件写入数据)

解释现象:

  • 一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。
  • printf fwrite 库函数会自带缓冲区(进度条例子就可以说明),当发生重定向到普通文件时,整个程序的数据的缓冲方式由行缓冲变成了全缓冲。
  • 而我们放在缓冲区中的数据,就不会被立即刷新,甚至fork被执行之后。
  • 但是进程退出之后,会统一刷新,写入文件当中
  • 但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的一份数据,随即产生两份数据
  • write 没有变化,说明write没有提供缓冲区。

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

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

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

相关文章

全面理解“张量”概念

1. 多重视角看“张量” 张量&#xff08;Tensor&#xff09;是一个多维数组的概念&#xff0c;在不同的学科领域中有不同的应用和解释&#xff1a; 物理学中的张量&#xff1a; 在物理学中&#xff0c;张量是一个几何对象&#xff0c;用来表示在不同坐标系下变换具有特定规律的…

(N-141)基于springboot,vue网上拍卖平台

开发工具&#xff1a;IDEA 服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 系统分前后台&#xff0c;项目采用前后端分离 前端技术&#xff1a;vueelementUI 服务端技术&#xff1a;springbootmybatis-plusredi…

GNS3连接Vmware虚拟机

1 安装配置Gns3、Vmware 安装过程略&#xff0c;最终版本号&#xff1a; Gns3&#xff1a;2.2.44.1 Vmware&#xff1a;17.0 建议保持一致&#xff0c;特别是Gns3&#xff0c;功能虽然强大的&#xff0c;但bug问题感觉也不少 2 虚拟机配置 新建两台Ubuntu 22.04虚拟机&#…

【JavaScript权威指南第七版】读书笔记速度

JavaScript权威指南第七版 序正文前言&#xff1a;图中笔记重点知识第1章 JavaScript简介第一章总结 第2章 词法结构注释字面量标识符和保留字Unicode可选的分号第二章总结 第3章 类型、值和变量【重要】原始类型特殊类型第三章总结 第4章 表达式与操作符表达式操作符条件式调用…

【JAVA面试精选篇-初生牛犊不怕虎】

文章目录 🌽 简介🧺 线程池🌄 Redis⏰ JVM🚛 数据结构🍎 Mysql🍡 结语🌽 简介 海阔凭鱼跃,天高任鸟飞! 学习不要盲目,让大脑舒服的方式吸收知识!!! 本人马上离开济南,回泰安发展,为了积极准备面试,目前在梳理一些知识点,同时希望能够帮助到需要的人… …

Rabbitmq调用FeignClient接口失败

文章目录 一、框架及逻辑介绍1.背景服务介绍2.问题逻辑介绍 二、代码1.A服务2.B服务3.C服务 三、解决思路1.确认B调用C服务接口是否能正常调通2.确认B服务是否能正常调用A服务3.确认消息能否正常消费4.总结 四、修改代码验证1.B服务异步调用C服务接口——失败2.将消费消息放到C…

智能AI系统开发,专业软件硬件物联网开发公司,探索未来科技新纪元

在信息时代&#xff0c;人工智能&#xff08;AI&#xff09;、物联网等前沿技术日益受到人们的关注。智能AI系统、专业软件硬件物联网开发公司应运而生。今天&#xff0c;我们将向大家介绍一家位于XX城的专业公司&#xff0c;致力于智能AI系统开发和软件硬件物联网领域的创新研…

大数据安全 | 期末复习(下)

文章目录 &#x1f4da;安全策略和攻击&#x1f34b;&#x1f407;安全协议&#x1f407;IPsee&#x1f407;SSL&#x1f407;SSH&#x1f407;S/MIME协议&#x1f407;公钥基础设施PKI&#x1f407;PGP&#x1f407;HTTPS&#x1f407;防火墙&#x1f407;防毒墙&#x1f407;…

解决:‘chromedriver’ executable needs to be in PATH

解决&#xff1a;chromedriver’ executable needs to be in PATH 文章目录 解决&#xff1a;chromedriver’ executable needs to be in PATH背景报错问题报错翻译报错位置代码报错原因解决方法方法一&#xff1a;检查python安装路径有没有添加到环境变量里面方法二&#xff1…

谷歌vue插件安装包

链接&#xff1a;https://pan.baidu.com/s/1wTCqn7ttc-rF_wZScfEgPw?pwde7k6 提取码&#xff1a;e7k6 修改D:\谷歌浏览器插件安装包\devtools-main\packages\shell-chrome下manifest.json文件 将里面这四个文件地址改为src下面&#xff0c;因为地址在src下&#xff0c;直接…

基于Guava布隆过滤器的海量字符串高效去重实践

在Java环境中处理海量字符串去重的问题时&#xff0c;布隆过滤器&#xff08;BloomFilter&#xff09;是一种非常高效的数据结构&#xff0c;尽管它有一定的误报率。布隆过滤器适用于那些可以接受一定误报率&#xff0c;并且希望节省空间和时间成本的场景。 布隆过滤器应用 使…

Fastjson代码审计实战

代码审计-漏洞复现 漏洞分析采用的是华夏ERP2.3&#xff0c; 查看pom.xml文件发现fastjson版本1.2.55&#xff0c;该版本存在漏洞&#xff0c;利用DNSlog进行验证。 fastjson涉及反序列化的方法有两种&#xff0c;JSON.parseObject()和JSON.parse()&#xff0c;在代码中直接搜…

MySQL 联合索引

文章目录 1.简介2.最左匹配3.最左匹配原理4.如何建立联合索引?5.覆盖索引参考文献 1.简介 联合索引指建立在多个列上的索引。 MySQL 可以创建联合索引&#xff08;即多列上的索引&#xff09;。一个索引最多可以包含 16 列。 联合索引可以测试包含索引中所有列的查询&#…

关于在Ubuntu20.04(ROS1 noetic)中使用catkin_make编译时发生的与pyhton版本不兼容的问题解决办法

今天在另外一台电脑上操作复现【ROS建模&#xff1a;一起从零手写URDF模型】这个博客时&#xff0c;发生了一些问题&#xff0c;特此记录下来 【ROS建模&#xff1a;一起从零手写URDF模型】链接&#xff1a;https://blog.csdn.net/qq_54900679/article/details/135726348?spm…

JasperReports渲染报表文件时候,读取图片报错:Byte data not found at:xxx.png.\r\n\tat

目录【知识星球】 1.1、错误描述 1.2、解决方案 1.1、错误描述 最近在工作中&#xff0c;使用JasperReports报表比较多一些&#xff0c;有次线上环境里面运行报错&#xff0c;查看报错日志&#xff0c;如下所示&#xff1a; net.sf.jasperreports.engine.JRException: Byte…

Spring Boot 启动错误【Kotlin】

目录 错误详情 错误原因 解决方法一 解决方法二 错误详情 Kotlin:     Module was compiled with an incompatible version of Kotlin.     The binary version of its metadata is 1.7.1, expected version is 1.1.16. 错误原因 编译器或构建工具的版本不匹配所造…

(免费领源码)python#Mysql苏州一日游之可视化分析69216-计算机毕业设计项目选题推荐

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对旅游服务等问题&#xff0c;对旅游服务进行…

docker 构建应用

docker 应用程序开发手册 开发 docker 镜像 Dockerfile 非常容易定义镜像内容由一系列指令和参数构成的脚本文件每一条指令构建一层一个 Dockerfile 文件包含了构建镜像的一套完整指令指令不区分大小写&#xff0c;但是一般建议都是大写从头到尾按顺序执行指令必须以 FROM 指…

Web3.0投票如何做到公平公正且不泄露个人隐私

在当前的数字时代&#xff0c;社交平台举办投票活动已成为了一种普遍现象。然而&#xff0c;随之而来的是一些隐私和安全方面的顾虑&#xff0c;特别是关于个人信息泄露和电话骚扰的问题。期望建立一个既公平公正又能保护个人隐私的投票系统。Web3.0的出现为实现这一目标提供了…

java web mvc-04-Apache Wicket

拓展阅读 Spring Web MVC-00-重学 mvc mvc-01-Model-View-Controller 概览 web mvc-03-JFinal web mvc-04-Apache Wicket web mvc-05-JSF JavaServer Faces web mvc-06-play framework intro web mvc-07-Vaadin web mvc-08-Grails 开源 The jdbc pool for java.(java …