【Linux 13】文件系统

文章目录

  • 🌈 一、前言
  • 🌈 二、文件操作的系统接口
    • ⭐ 1. 打开文件 open
    • ⭐ 2. 关闭文件 close
    • ⭐ 3. 写入文件 write
    • ⭐ 4. 读取文件 read
  • 🌈 三、文件描述符
    • ⭐ 1. 文件描述符介绍
    • ⭐ 2. 提前被分配的文件描述符 0 1 2
    • ⭐ 3. 文件描述符的分配规则
  • 🌈 四、重定向和缓冲区
    • ⭐ 1. 重定向
    • ⭐ 2. 缓冲区
    • ⭐ 3. dup2 实现直接重定向
  • 🌈 五、文件系统
    • ⭐ 1. 对磁盘进行分区管理
    • ⭐ 2. 对磁盘分区进行分组管理
    • ⭐ 3. 如何进行分组管理
    • ⭐ 4. inode 介绍
  • 🌈 六、软硬链接
    • ⭐ 1. 硬链接
    • ⭐ 2. 软链接
  • 🌈 七、动静态库
    • ⭐ 1. 静态库
      • 1.1 静态库介绍
      • 1.2 生成静态库
      • 1.3 使用静态库
    • ⭐ 2. 动态库
      • 2.1 动态库介绍
      • 2.2 生成动态库
      • 2.3 使用动态库

🌈 一、前言

1. 文件的概念

  1. 文件 = 内容 + 属性,所有对文件的操作无非就是两种:对文件内容操作、对文件属性操作。
  2. 文件的内容和属性本质上都是数据。存储文件必须既存储内容数据,又存储属性数据。
  3. 进程想要访问一个文件的时候,都需要先将该文件打开,文件在打开前后处于两种状态。
    • 打开文件前:文件处在磁盘当中,就是个普通的文件,进程无权访问。
    • 打开文件后:将文件加载到内存,此时进程才有权力访问该文件的数据。
  4. 进程可以打开多个文件,被打开的文件可能会存在多个,因此就需要管理起来。
    • 文件在被打开前,必须要先形成一个用于描述被打开文件的结构体
    • 进程打开文件的操作是通过 OS 执行的,因此 OS 一定要提供系统调用接口。

2. 管理被打开的文件

  • 每一个被打开的文件都要创建对应的描述结构体。
  • 将这些结构体使用链表管理,因此对被打开文件的管理就变成了对链表的管理。

在这里插入图片描述

🌈 二、文件操作的系统接口

  • 对文件进行操作,除了调用 C 接口(C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问。

系统接口需要的头文件

  • 很显然需要的头文件比直接调用 C 语言的接口需要的多,说明语言会对这些接口进行封装。
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

⭐ 1. 打开文件 open

1. 函数参数

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>int open(const char* pathname, 	// 表示要打开的文件的所在路径,如果只有文件名则默认在当前路径int flags, 				// 表示打开文件的方式mode_t mode);			// 给文件赋予权限,可不提供该参数 (新文件权限随机,旧文件不变)

2. flags 参数的常用选项及其功能

  1. 必须的选项: 只能指定其中一个,以下三个选项同时只能存在一个,但每个都可以与可选的选项进行组合。
选项说明
O_RDONLY以只读的方式打开文件
O_WRONLY以只写的方式打开文件
O_RDWR以读写的方式打开文件
  1. 可选的选项:可以指定零个或多个,以下选项通过或操作实现执行多个选项
    • 如:O_WRONLY | O_CREAT | O_TRUNC 表示同时执行这 3 个选项的功能。
选项说明
O_CREAT如果文件不存在,则创建它。需要第三个参数 mode 来指定新文件的权限。
O_EXCL和 O_CREAT 一起使用时,如果文件已存在,则 open 失败,确保文件是新建的。
O_TRUNC如果文件已存在且以写方式打开,则将文件内容清空。
O_APPEND以追加的方式打开文件,写入的数据会添加到文件末尾。
O_NONBLOCK对于设备文件,以非阻塞方式打开
O_SYNC将写操作同步到磁盘。
O_DSYNC类 O_SYNC,但只同步写入操作,不包括元数据的更新

3. 函数返回值

  • open 接口会返回一个文件描述符,如果返回 -1 则表示文件打开失败。

4. 使用实例

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>int main()
{// 在当前目录以写方式打开文件,如果该文件不存在则创建并赋予 270 的权限int fd = open("log.txt", O_WRONLY | O_CREAT, 0270);// 打开文件失败if (fd < 0){perror("open");return 1;}// 操作文件...// 关闭文件close(fd);return 0;
}

在这里插入图片描述

⭐ 2. 关闭文件 close

1. 函数参数

#include <unistd.h>int close(int fd);	// 关闭文件描述符 fd 所表示的文件

2. 函数返回值

  • 关闭文件成功:返回 0
  • 关闭文件失败:返回 -1

⭐ 3. 写入文件 write

  • 向一个文件描述符描述的文件当中进行写入。

1. 函数参数

#include <unistd.h>ssize_t write(int fd, 		 // 文件描述符,表示要写入的是哪个文件const void *buf, // 要写入到文件中的字符串的起始地址size_t count);   // 要写入到文件当中的字节数

2. 函数返回值

  • write 函数的返回值表示实际写入到文件中的字节数

3. 函数用例

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>int main()
{// 打开文件int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);if (fd < 0) // 打开文件失败{perror("open");return 1;}// 操作文件 (将 msg 写入 fd 所描述的文件)const char* msg = "hello file system call\n";write(fd, msg, strlen(msg));// 关闭文件close(fd);return 0;
}

在这里插入图片描述

⭐ 4. 读取文件 read

  • 从指定文件当中读取指定字节的内容到字符串中。

1. 函数参数

#include <unistd.h>ssize read(int fd, 		// 从文件描述符 fd 指定的文件中读取内容void* buf, 		// 将读取到的内容放到 buf 中size_t count);	// 指明希望从文件中读取的字节数

2. 函数返回值

  • 读取成功:read 函数的返回值表示实际从文件中读取到的字节数
  • 读取失败:read 函数的返回值是 -1。

3. 函数用例

  • 当前有一个内容为 hello world! 的文件 log.txt,现在要使用 read 读取该文件的内容并放入到指定字符串中。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>#define FILE_NAME "log.txt"int main()
{// 以只读方式打开该文件int fd = open(FILE_NAME, O_RDONLY);// 从指定文件中读取 100 个字节的内容放到 buffer 中char buffer[1024];ssize_t n = read(fd, buffer, 100);printf("实际读取字节数: %d, 读取内容: %s\n", n, buffer);// 关闭文件close(fd);return 0;
}

在这里插入图片描述

🌈 三、文件描述符

⭐ 1. 文件描述符介绍

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

在这里插入图片描述

⭐ 2. 提前被分配的文件描述符 0 1 2

  • Linux 进程在默认清空下会有 3 个默认打开的文件描述符,分别是标准输入 0 (stdio)标准输出 1 (stdout)标准错误 2 (stderr)
  • 0、1、2 对应的物理设备一般是:键盘、显示器、显示器
  • 也就是说,我们自己的文件的文件描述符都是从 3 开始计数的。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>int main()
{// 分别创建 4 个文件,并用 4 个下标处的文件指针指向这些文件int fd1 = open("log1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);int fd2 = open("log2.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);int fd3 = open("log3.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);int fd4 = open("log4.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);// 打印默认 3 个打开文件和 4 个自己文件的文件描述符printf("stdin: %d\n", stdin->_fileno);  // 标准输入printf("stdout: %d\n", stdout->_fileno);// 标准输出printf("stderr: %d\n", stderr->_fileno);// 标准错误printf("fd1: %d\n", fd1);printf("fd2: %d\n", fd2);printf("fd3: %d\n", fd3);printf("fd4: %d\n", fd4);// 关闭 4 个文件描述符所指向的文件close(fd1);close(fd2);close(fd3);close(fd4);return 0;
}

在这里插入图片描述

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

1. 分配规则

  • 在 files_struct 数组当中,找到当前未被使用最小下标,作为新的文件描述符。
    • 如果直接把 0 号描述符所描述的文件观点,此时最小未被使用下标就是 0,下次再打开新文件,该文件的文件描述符就是 0。
int main()
{close(0);   // 关闭标准输入文件int fd = open("log.txt", O_RDONLY);printf("新打开文件的文件描述符: %d\n", fd);close(fd);return 0;
}

在这里插入图片描述

🌈 四、重定向和缓冲区

⭐ 1. 重定向

1. 关闭 1 号文件

  • 由于 1 号文件表示的是标准输出 stdout 即显示器,如果将显示器文件关闭,之后新打开文件的文件描述符就会是 1。
  • 那么所有原本要输出到显示器上的内容都会被重定向输出到新打开文件中。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>#define FILE_NAME "log.txt"int main()
{close(1);                           // 关闭显示器文件int fd = open(FILE_NAME, O_WRONLY); // 以只读方式打开指定文件printf("fd: %d\n", fd);             // printf 只认 1 号文件,向 1 号文件写入fflush(stdout);                     // 刷新缓冲区,立马将缓冲区数据交给文件close(fd);                          // 关闭新打开的文件return 0;
}

在这里插入图片描述

2. 重定向的本质

  • 重定向的本质就是修改文件描述符表特定数组下标中的内容
  • 让某个下标中存着的 FILE* 指针从指向 A 文件,变成指向 B 文件就是重定向。

在这里插入图片描述

⭐ 2. 缓冲区

  • 输出内容不是直接将内容输出到文件的,而是先将内容存放在缓冲区中,等到缓冲区囤积了一定量的数据之后才一次性交给文件。
  • 在重定向的代码中,如果不使用 fflush(stdout) 冲刷缓冲区,那么并不会立马将缓冲区内容输出到自己的 log.txt 文件中。
int main()
{close(1);                                           int fd = open(FILE_NAME, O_WRONLY | O_TRUNC);printf("fd: %d\n", fd);// fflush(stdout); // 不立马将缓冲区中的数据交给文件close(fd);return 0;
}

在这里插入图片描述

  • printf 输出的内容是会先存放在输出缓冲区 stdout 中的,然后再将缓冲区中的内容刷新到 log.txt 文件中。
  • 在进程退出前,调用了 close(fd)。如果不使用 flush 强制将缓冲区中的内容立刻刷新到 log.txt 中,那么在关闭文件之后,缓冲区的内容自然也就进不到文件中。
  • 进程在退出时一般是要刷新缓冲区的,但是此时 log.txt 文件已经被关闭了,也无法将缓冲区的内容刷给 log.txt 了,因此 log.txt 文件中就什么也没有了。

⭐ 3. dup2 实现直接重定向

  • 如果每次执行重定向都要利用文件描述符的分配规则 (先将文件描述符靠前文件关闭,再打开自己的文件),未免显得太折磨了点。

1. 如何直接实现重定向

  • 使用dup2 系统调用函数更改文件描述符数组下标对应的内容 ( file* 文件指针所指向的文件) 来完成重定向。
    • 如:3 号文件指向的是 log.txt 文件,只需要将 3 号下标的内容拷贝覆盖到 1 号,就可以直接实现重定向。

在这里插入图片描述

2. dup2 函数介绍

#include <unistd.h>int dup2(int oldfd, int newfd);
  • 将文件描述符数组中 oldfd 下标处的内容拷贝覆盖到 newfd 下标处,使得两个下标处的内容都和 oldfd 的一样。

3. dup2 函数用例

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>#define FILE_NAME "log.txt"int main()
{// 以追加写的方式打开 log.txt 文件int fd = open(FILE_NAME, O_WRONLY | O_APPEND);// 将 fd 下标处的内容覆盖到下标 1 处实现输出重定向// 这样之后往 1 号文件输出的内容就变成了往 log.txt 文件输出dup2(fd, 1);printf("hello printf\n");fprintf(stdout, "hello fprintf\n");close(fd);return 0;
}

在这里插入图片描述

🌈 五、文件系统

文件管理工作 (文件系统)

  1. 对打开的被加载到内存中的文件进行管理。
  2. 对未打开的在硬盘中的文件也要进行管理。

⭐ 1. 对磁盘进行分区管理

  • 没打开的文件都在磁盘上放着,也需要在磁盘中进行管理
    • 未被进程打开的文件在磁盘中按照一定规律存储,方便用户随时读取。
  • 这于这部分未打开文件核心工作快速定位文件
    • 快速定位文件是通过查找文件路径实现的。
  • 磁盘都是很大的,500G 的空间如果要全部管理起来会很费劲,但是由于所有的磁盘区域都能够使用同一个管理方法,因此可以对磁盘使用分区管理
    • 500G 和 100G 的管理方法一样,将 500G 分成 5 个 100G 管理就会轻松很多。
    • 如:将一块磁盘分成 C D E F … 盘,管理起来明显会变轻松。

在这里插入图片描述

⭐ 2. 对磁盘分区进行分组管理

  • 管理一块 500G 的磁盘可以复用管理一块 100G 区域的管理方式,这 100G 的空间也可以划分成块组进行管理
  • 不同文件系统有不同的分组方式,此时假设以 2G 为一块组对一个 100G 的磁盘分区划分出 50 个块组。
  • 只要能管理好 1 个组,就能管理 50 个块组 (1 个磁盘分区),能管理好 1 个磁盘分区就能管理好整个磁盘。

在这里插入图片描述

⭐ 3. 如何进行分组管理

1. 每个块组分为如下部分

  1. 超级块 (Super Block):存放文件系统本身的结构信息,只有个别和文件系统有关的块组有这玩意。
    • 超级块记录的信息主要有:block 和 inode 的总量, 未使用的 block 和 inode 的数量,一个 block 和一个 inode 的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。
    • 超级块的信息一旦被破坏,整个文件系统的结构就会被破坏。
  2. 块组描述符表 (Group Descriptor Table):描述块组的使用情况。
  3. 块位图 (Block Bitmap):用位图记录 Data Blocks 中哪个数据块已经被占用,哪个没被占用。
  4. inode 位图 (inode Bitmap):每个 bit 都表示在 inode 表中的一个 inode 是否空闲可用。
  5. inode 表 (inode Table):inode 是一个用来存放文件属性的结构体,而 inode Table 则是用来存放 inode 的一个结构具体数组。
  6. 数据区 (Data Blocks):存放文件内容。整个数据区被分成若干个 4KB 大小的数据块。

在这里插入图片描述

2. 属性和数据分开存放

  • 可以看出,每个块组都将属性和数据分开存放,这操作看着简单实际一点也不复杂。

⭐ 4. inode 介绍

1. inode 编号

  • 一般情况下,每个文件都有属于自己的 inode 编号,inode 编号在整个分区具有唯一性。
  • Linux 在内核当中,识别文件,和文件名无关,只认 inode 编号
  • 如何通过文件名找到 inode 编号?:每个目录内都会保存目录内文件的文件名和 inode 编号的映射关系,通过文件名即可映射找到对应的 inode 编号,这也是为什么同一个目录下不允许有同名文件的原因。

在这里插入图片描述

2. inode 结构体

  • 每个文件都有自己的属性和内容,属性的种类是有限的 & 每个文件都一样。
  • inode 结构体 就是用来存放文件属性的。
  • 每个 inode 结构体 的固定大小都是 128 字节
// 文件属性是通过 inode 结构体保存的
// 每个 struct inode 的大小都是 128 字节
struct inode
{// 文件属性: 文件大小、权限、所属组、ACM 时间、inode 编号等blocks[15];	// 记录 Data Blocks 中有哪些数据块属于同一个 inode 编号所表示的文件
};

3. inode 表 (inode table)

  • inode 节点表,顾名思义就是一张用来存放 inode 结构体的表 (数组),inode 编号就是这个数组的下标。

🌈 六、软硬链接

1. 链接介绍

  • 真正在磁盘上找文件靠的并不是文件名,而是 inode,只不是文件名映射了对应的 inode。
  • 在 Linux 中可以让多个文件名对应同一个 inode (通过不同的名称访问相同的文件或目录内容)。这种操作称之为链接

2. 为什么要存在链接

  • 某些文件会存储在很深的路径底下,不方便寻找。可以通过链接的方式链接这个藏很深的文件,然后将链接文件放在桌面 (或其他地方),实现在桌面点击快捷方式直接访问该文件。

3. 链接的使用场景

  • 快速定位文件。

⭐ 1. 硬链接

  • 硬链接通过 inode 编号引用另一个文件,该链接方式不能应用于目录,只能应用于文件
  • 硬链接不是独立的文件,因为它没有自己的 inode 编号。

0. 硬链接是什么?

  • 硬链接是在指定目录内部一组映射关系 (文件名 <=> inode 编号)。

1. 建立硬链接命令

ln 目标文件路径名 链接名 // 用自己定义的链接文件去链接目标文件

2. 建立硬链接示例

  • 让 hello.hard.link 文件去链接 hello 文件,这两个文件都是同一个文件,有相同的 inode 编号。

在这里插入图片描述

  • 建立硬链接相当于对 2228447 这个文件进行重命名,建立硬链接之后就可以删除老文件名了。

3. 硬链接使用示例

  • 往 hello.hard.link 中写入一串字符,使用 cat 打印 hello.hard.link 和 hello 文件中的内容。

在这里插入图片描述

4. 删除硬链接命令

unlink 硬链接名

⭐ 2. 软链接

  • 不同于硬链接,软链接通过名字引用另一个文件
  • 软链接是独立的文件,因为它有自己的 inode 编号。

1. 建立软链接命令

ln -s 目标文件或目录路径名 链接名 // 用自己定义的链接文件去链接目标文件或目录

2. 建立软链接示例

  • 让 log.soft.link 文件去链接 log 文件,这两个文件都是独立的文件,有不同的 inode 编号。

在这里插入图片描述

3. 软链接使用示例

  • 往 log.soft.link 文件中写入一串字符,log 文件和 log.soft.link 文件都发生了变化。

在这里插入图片描述

🌈 七、动静态库

⭐ 1. 静态库

1.1 静态库介绍

  • 静态库(.a 文件):编译时链接,程序在编译链接的时候把库的代码链接到可执行文件中。程序运行时不再需要静态库

1.2 生成静态库

  • 以制作一个简单的计算器为例。

1. 创建相关文件

在这里插入图片描述

2. 编写相关文件

  1. 编写 add.h 和 add.c (剩下几个文件逻辑一样,就不贴出来了)。

在这里插入图片描述

3. 编译相关文件

  • 只用将 add.c、sub.c、mul.c、div.c、编译成 .o 文件即可。

在这里插入图片描述

4. 生成静态库

  • 将所有的 .o 文件进行链接形成一个可执行。
  • 生成静态库指令:ar -rc libmylib.a file1.o fil2.o ... filen.o
    • mylib 是你的静态库文件名 (名字可自定义)。
    • file1.o ~ filen.o 是生成静态库文件时所依赖的编译文件,在本例中是 add.o、sub.o、mul.o、div.o 文件。

在这里插入图片描述

  • mylib 就是生成的静态库的真实名字。

1.3 使用静态库

1. 编写 & 编译测试文件

  • 创建一个 main.c 文件,在该文件中使用上述加减乘除 4 个模块的库函数。
#include "add.h"
#include "sub.h"
#include "mul.h"
#include "div.h"int main()
{int x = 10, y = 20;printf("%d + %d = %d\n", x, y, add(x, y));printf("%d - %d = %d\n", x, y, sub(x, y));printf("%d * %d = %d\n", x, y, mul(x, y));printf("%d / %d = %d\n", x, y, div(x, y));return 0;
}

2. 编译测试文件并与静态库链接

  • 使用如下指令将 main.c 文件编译并将编译后的文件与静态库 mylib 链接。
    • 库搜索路径: -L 指定库所在的路径,-l 表示链接指定的库
g++ -o test.exe main.c -L. -lmylib

在这里插入图片描述

3. 删除静态库

  • 目标文件生成后,静态库删掉,程序照样可以运行。因为静态库中的代码已经全部加载到生成的可执行文件中了。

在这里插入图片描述

⭐ 2. 动态库

2.1 动态库介绍

  • 动态库(.so 文件):运行时链接,程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
  • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。
  • 操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间

2.2 生成动态库

  • 生成动态库的例子依然是制作一个简单的计算器,所建立的相关文件依然是下面这些。
加法模块 : add.h add.c 
减法模块 : sub.h sub.c
乘法模块 : mul.h mul.c
除法模块 : div.h div.c
测试模块 : main.c

1. 生成动态库指令

  1. 编译源代码为位置无关的代码 (PIC):
    • 在使用 gcc / g++ 编译文件时,使用 -fPIC 选项编译你的源代码文件,生成 .o 文件。
gcc -fPIC -c source.c -o source.o
g++ -fPIC -c source.c -o source.o
  1. 生成动态库:使用 -shared 选项gcc / g++ 将对象文件链接成动态库 (.so 文件)。
    • 其中,libmylib.so 文件为动态库文件名,开头的 lib 和结尾的 .so 为必带选项,中间的 mylib 才是能让你自定义的动态库名。
    • source1.o ~ sourcen.o 是生成动态库所要依赖的编译对象文件,可依赖多个。
gcc -shared -o libmylib.so source1.o source2.o sourcen.o
g++ -shared -o libmylib.so source1.o source2.o sourcen.o

2. 生成动态库实例

  1. 编译源代码为位置无关代码:将 add.c、sub.c、mul.c、div.c 分别编译成各自对应的 .o 文件。

在这里插入图片描述

  1. 生成动态库:依赖编译出的 4 个 .o 文件生成动态库。

在这里插入图片描述

2.3 使用动态库

1. 链接动态库指令

  • 在使用 gcc / g++ 编译测试文件时,使用 -L 和 -l 选项链接动态库。
    • -L 和 -l 选项的功能和链接静态库时一样,就不过多赘述。
gcc -o myprogram main.c -L. -lmylib
g++ -o myprogram main.c -L. -lmylib

2. 链接动态库实例

在这里插入图片描述

3. 运行动态库

  • 在运行使用动态库的程序前,需要确保动态库文件(libmymath.so)在库搜索路径中
    1. 方法 1:将 .so 文件拷贝到系统共享库路径下, 一般指 /usr/lib (不推荐把自己写的库放进去,推荐放别人的)。
    2. 方法 2:更改 LD_LIBRARY_PATH 环境变量来指明 .so 文件所在路径。
  • 这里就采用第 2 中方法来运行动态库。

在这里插入图片描述

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

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

相关文章

浏览器插件利器--allWebPluginV2.0.0.16-beta版发布

allWebPlugin简介 allWebPlugin中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件产品&#xff0c;致力于将浏览器插件重新应用到所有浏览器。它将现有ActiveX控件直接嵌入浏览器&#xff0c;实现插件加载、界面显示、接口调用、事件回调等。支持Chrome、Firefo…

minio安装小计一则

安装minio并且使用api方式对文件进行操作 本文使用docker安装 docker pull minio/minio docker pull minio/mc &#xff08;mc为minio文件运行命令行工具&#xff09; 使用如下命令运行Minio服务器容器&#xff1a; docker run -p 9000:9000 -p 9001:9001 --name minio-…

【深度学习】大模型GLM-4-9B Chat ,微调与部署

下载好东西&#xff1a; 启动容器环境: docker run -it --gpus all --net host --shm-size8g -v /ssd/xiedong/glm-4-9b-xd:/ssd/xiedong/glm-4-9b-xd kevinchina/deeplearning:pytorch2.3.0-cuda12.1-cudnn8-devel-yolov8train bashpip install typer tiktoken numpy1.2…

Web开发:ASP.NET CORE使用Ajax定时获取后端数据

一、低难度&#xff08;刷新a标签&#xff09; 1、需求 给a标签每15s刷新一次&#xff0c;显示最新的时间&#xff08;时间必须由后端获取&#xff09; 应该如何操作呢 2、代码 后端 using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Mi…

解决mysql5.0,Navicat for MySQL,IntelliJ IDEA之间中文乱码

使用软件版本 jdk-8u171-windows-x64 ideaIU-2021.1.3 mysql-essential-5.0.87-win32 navicat8_mysql_cs 这个问题我调试了好久&#xff0c;网上的方法基本上都试过了&#xff0c;终于是解决了。 三个地方结果都不一样。 方法一 首先大家可以尝试下面这种方法&#xff1a…

记录 cocos 开发问题 ,微信 wx.xxx函数 报找不到名称“wx”

今天写微信排行榜遇到 问题分享一下。 目前&#xff0c;微信、百度 和 抖音 小游戏这些平台为了保护其社交关系链数据&#xff0c;增加了 开放数据域 的概念&#xff0c;这是一个单独的游戏执行环境。开放数据域中的资源、引擎、程序&#xff0c;都和主游戏&#xff08;主域&a…

(秋招复习)自动驾驶与机器人中的SLAM技术(二)

秋招复习之--自动驾驶与机器人中的SLAM技术2 第五章 基础点云处理激光传感器与点云的数学模型最近邻问题准确率和召回率暴力最近邻栅格和体素方法二分树与K-d树四叉树和八叉树 拟合问题平面拟合直线拟合 第六章 2D激光定位与建图扫描匹配算法点到点的scan matching点到线的scan…

mybatis-plus模板引擎代码生成

网盘代码&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1jwuVGiA97dc1KVnGKc0c4g?pwd6666 提取码&#xff1a;6666 Gradle依赖&#xff1a; dependencies {// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starterimplementation org.…

linux系统设置开机启动的两种方法systemd及rc.local(手工写sh脚本,手工写service服务)

文章目录 知识点实验一、systemd&#xff08;一&#xff09;自写一个sh脚本并加入开机启动&#xff08;二&#xff09;源码安装的nginx加入开机启动 rc.local 知识点 在Linux系统中&#xff0c;有多种方法可以设置开机启动。以下是其中的一些主要方法&#xff1a; systemd 在较…

基于SSM的网上选课系统

系统背景 在当今信息化高速发展的时代&#xff0c;随着Internet的普及和高等教育规模的不断扩大&#xff0c;传统的手工选课方式已难以满足高校日益增长的管理需求。传统的选课方式不仅效率低下&#xff0c;还容易出现人为错误&#xff0c;导致资源浪费和管理成本上升。因此&am…

2959.力扣每日一题7/17 Java(暴力枚举+Floyd算法)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;算法练习关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 目录 Floyd算法 解题思路 解题过程 时间复杂度 空间复杂度 Floyd算法 …

Linux系统快速搭建轻量化网站Halo并实现无公网IP远程访问

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

AWE2025正式启动,AWE×AI 推动智慧生活的普及

7月18日&#xff0c;2025年中国家电及消费电子博览会&#xff08;AWE2025&#xff09;正式启动。主办方宣布&#xff0c;AWE2025的主题为“AI科技、AI生活”&#xff0c;展会将于2025年3月20-23日在上海新国际博览中心举办。 作为全球三大家电和消费电子领域展会之一&#xff…

一个非常好的美图展示网站整站打包源码,集成了wordpress和开源版ripro主题,可以完美运营。

一个非常好的美图展示网站整站打包源码&#xff0c;集成了wordpress和开源版ripro主题&#xff0c;可以完美运营。 自带了5个多g的美图资源&#xff0c;让网站内容看起来非常大气丰富&#xff0c;可以快速投入运营。 这个代码包&#xff0c;原网站已经稳定运营多年&#xff0…

Linux_生产消费者模型

目录 1、生产消费者模型示意图 2、生产者消费者之间的关系 3、定义交易场所 4、实现生产消费者模型 5、伪唤醒 6、多生产多消费者的实际运用 7、POSIX信号量 7.1 初始化信号量 7.2 销毁信号量 7.3 等待信号量 7.4 发布信号量 8、生产消费的环形队列模型 8.1…

Codeforces Round 942 (Div. 2)

比赛链接&#xff1a;Dashboard - Codeforces Round 942 (Div. 2) - Codeforces A题 翻译中文题面&#xff1a; 一场比赛包含 n 个问题&#xff0c;第 i 个问题的难度预期最多为 bi。已经有 n 个问题的提议&#xff0c;第 i 个问题的难度是 ai。最初&#xff0c;数组 a1,a2,……

安全与便捷并行,打造高效易用的用户支付体验

在当今数字时代&#xff0c;快捷、安全的支付方式已经成为用户日常生活中不可或缺的一部分。不论是在线购物、订阅服务&#xff0c;还是线下消费&#xff0c;用户都期望享受流畅且安全的支付体验。作为开发者&#xff0c;选择适合的支付服务不仅关乎用户体验&#xff0c;更直接…

漏洞预警:Nacos 0day漏洞触发远程代码执行

Nacos即Dynamic Naming and Configuration Service&#xff08;动态命名与配置服务&#xff09;&#xff0c;是开源的一款服务发现、配置和管理微服务的中间件。 在Nacos中新发现的0day漏洞可以触发远程代码执行&#xff0c;开源网安RASP团队检测并分析出三种类型的攻击&#x…

MySQL学习(13):SQL优化:查看SQL语句性能的方法

1.查看SOL执行频率 MySQL客户端连接成功后&#xff0c;通过如下指令&#xff0c;可以查看当前数据库的insert、update、delete、select的访问频次: show global status like Com_______; #查看全局。后面是7个下划线 使用效果如下&#xff1a; 可以看到各条命令的使用次数。…

android13读取cpu频率,并调整频率

总纲 android13 rom 开发总纲说明 目录 1.前言 2.频率类型 3.获取cpu可以调节的频率 4.获取当前频率 5.设置频率 6.最后我们写个脚本,来实现,可以通过参数获取所有cpu的频率,以及设置最大最小频率 6.1 获取cpu频率 6.2 设置最大cpu频率 6.3 设置最小 7.彩蛋 1.前…