【Linux 13】文件系统

news2024/11/13 12:49:21

文章目录

  • 🌈 一、前言
  • 🌈 二、文件操作的系统接口
    • ⭐ 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.coloradmin.cn/o/1941268.html

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

相关文章

【Qt】Qt的坐标转换(mapToGlobal)

1、QPoint QWidget::mapToGlobal(const QPoint &pos) const 将小部件坐标转换为全局坐标。mapToGlobal(QPoint(0,0))可以得到小部件左上角像素的全局坐标。2、QPoint QWidget::mapToParent(const QPoint &pos) const 将小部件坐标转换为父部件坐标。如果小部件没有父部…

Linux内存的概念及管理

1、内存概念 内存是指计算机中所安装的随机存取内存的容量&#xff0c;储存是指计算机内硬盘的容量。硬盘应当是计算机的“外存”。内存应当是在主板上的一些存储器&#xff0c;用来保存CPU运算使用过程中的中间数据和计算结果&#xff0c;当不用这些数据时&#xff0c;它们被保…

鸿蒙仓颉语言【Redis仓颉语言客户端】

特性 支持RESP2和RESP3协议接口设计兼容jedis接口语义丰富的管理命令支持支持单连接多线程模式支持发布订阅模式支持哨兵模式和集群模式完备的单元测试覆盖架构简洁&#xff0c;易于扩展 开发计划 2024.3.22 完成支持单机模式的RESP2和RESP3协议的客户端&#xff0c;提供Bet…

实际生活中网段不通的典型分析及处理方案

关于端口&#xff1a; 应用层&#xff1a; FTP TELNET SMTP DNS TFTP SNMP 端口号&#xff1a; 21 23 25 53 69 161 传输层&#xff1a; TCP UDP&#xff08;DNS两个都占…

7月21日,贪心练习

大家好呀&#xff0c;今天带来一些贪心算法的应用解题、 一&#xff0c;柠檬水找零 . - 力扣&#xff08;LeetCode&#xff09; 解析&#xff1a; 本题的贪心体现在对于20美元的处理上&#xff0c;我们总是优先把功能较少的10元作为找零&#xff0c;这样可以让5元用处更大 …

野兔在线工具箱系统全新升级改版,基于TP8和yetuadmin后台实现

野兔在线工具箱系统全新升级改版&#xff0c;基于TP8和yetuadmin后台实现 系统名称&#xff1a;野兔在线工具系统 系统语言&#xff1a;支持多语言&#xff0c;大概有20种 系统源码&#xff1a;不加密&#xff0c;开源 系统开发&#xff1a;PHPMySQL (基于thinkphp8&#x…

重发布路由策略实验

实验要求 1.搭建拓扑 路由策略分析&#xff1a; 拓扑左边是rip协议&#xff0c;右边是ospf协议&#xff0c;想要实现全网可达可以采用多点双向重发布的方式。 对于rip协议使用偏移列表来干涉选路&#xff0c;对于ospf协议采用路由策略来干涉选路 2.配置ip r1 [AR1]interfac…

【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理

初阶数据结构相关知识点可以通过点击以下链接进行学习一起加油&#xff01;时间与空间复杂度的深度剖析深入解析顺序表:探索底层逻辑深入解析单链表:探索底层逻辑深入解析带头双向循环链表:探索底层逻辑深入解析栈:探索底层逻辑深入解析队列:探索底层逻辑深入解析循环队列:探索…

什么是机器学习以及机器学习如今的社会现状!!

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

长效代理IP如何选用及代理服务分析

在这个数据为王、信息瞬息万变的时代&#xff0c;长效代理IP成为了众多开发者、数据科学家乃至普通网民手中的一把利器。它不仅能帮助我们解决地域管理&#xff0c;还能在保护隐私的同时&#xff0c;确保数据传输的稳定与安全。但面对市面上琳琅满目的代理服务&#xff0c;如何…

飞腾腾云S2500 Nginx单机环回测试性能调优方法

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力&#xff0c;聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域&#xff0c;包含了应用使能套件、软件仓库、软件支持、软件适…

Vulnhub靶场DC-7练习

目录 0x00 准备0x01 主机信息收集0x02 站点信息收集1. 获取用户名/密码2. ssh连接目标主机3. drush命令修改Drupal密码 0x03 漏洞查找与利用1. Drupal写入php木马2. 连接shell3. 反弹shell并提权 0x04 总结 0x00 准备 下载链接&#xff1a;https://download.vulnhub.com/dc/DC-…

深度学习每周学习总结N4:中文文本分类-Pytorch实现(基本分类(熟悉流程)、textCNN分类(通用模型)、Bert分类(模型进阶))

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 目录 0. 总结&#xff1a;1. 基础模型a. 数据加载b. 数据预处理c. 模型搭建与初始化d. 训练函数e. 评估函数f.拆分数据集运行模型g. 结果可…

git命令学习分享

分布式版本控制系统&#xff0c;本地仓库和远程仓库相互独立。 使用repository仓库进行控制&#xff0c;可以对里面的文件进行跟踪&#xff0c;复原。 git config --global --list&#xff1a;查看git配置列表 cd ** &#xff1a;进入** cd .. &#xff1a;退回上一级 echo…

【人工智能】Transformers之Pipeline(四):零样本音频分类(zero-shot-audio-classification)

​​​​​​​ 目录 一、引言 二、零样本音频分类&#xff08;zero-shot-audio-classification&#xff09; 2.1 概述 2.2 意义 2.3 应用场景 2.4 pipeline参数 2.4.1 pipeline对象实例化参数​​​​​​​ 2.4.2 pipeline对象使用参数 2.4 pipeline实战 2.5 模…

TinyVue:与 Vue 交往八年的组件库

本文由体验技术团队莫春辉老师原创~ 去年因故停办的 VueConf&#xff0c;今年如约在深圳举行。作为东道主 & 上届 VueConf 讲师的我&#xff0c;没有理由不来凑个热闹。大会结束后&#xff0c;我见裕波在朋友圈转发 Jinjiang 的文章《我和 Vue.js 的十年》&#xff0c;我就…

版本控制工具

版本控制工具是用于记录代码文件变化历史、方便查阅特定版本修改情况的系统&#xff0c;一般分为集中式和分布式两种。以下是一些常见的版本控制工具&#xff1a; 集中式版本控制工具 Subversion&#xff08;SVN&#xff09; 简介&#xff1a;Subversion是一种集中式版本控制…

【LeetCode】day15:110 - 平衡二叉树, 257 - 二叉树的所有路径, 404 - 左叶子之和, 222 - 完全二叉树的节点个数

LeetCode 代码随想录跟练 Day15 110.平衡二叉树257.二叉树的所有路径404.左叶子之和222.完全二叉树的节点个数 110.平衡二叉树 题目描述&#xff1a; 给定一个二叉树&#xff0c;判断它是否是 平衡二叉树 平衡二叉树的定义是&#xff0c;对于树中的每个节点&#xff0c;其左右…

文件包含漏洞: 函数,实例[pikachu_file_inclusion_local]

文件包含 文件包含是一种较为常见技术&#xff0c;允许程序员在不同的脚本或程序中重用代码或调用文件 主要作用和用途&#xff1a; 代码重用&#xff1a;通过将通用函数或代码段放入单独的文件中&#xff0c;可以在多个脚本中包含这些文件&#xff0c;避免重复编写相同代码。…

昇思25天学习打卡营第27天 | Diffusion扩散模型

学习心得&#xff1a;探索Diffusion扩散模型 在我最近对生成模型的学习中&#xff0c;尤其是Diffusion模型&#xff0c;我发现这是一种极具潜力的技术&#xff0c;特别是在图像生成领域。Diffusion模型的核心概念是通过一个逐步的去噪过程&#xff0c;将纯噪声数据转换成有意义…