文章目录
目录
前言
在软件开发领域,随着项目规模不断扩大、功能日益复杂,代码的复用性和管理成为至关重要的问题。为了避免重复编写相同功能的代码,提高开发效率,动静态库应运而生。
一、动静态库是什么?
• 静态库:它是一组目标文件的集合,在程序编译链接阶段,会将静态库中的代码直接复制到最终的可执行文件中。这意味着可执行文件在运行时不再依赖于静态库本身,拥有相对独立完整的代码体系。例如,在开发一些简单的命令行工具时,可能会将常用的字符串处理函数等打包成静态库供不同工具复用。
• 动态库:与静态库不同,动态库在程序编译时并不会把库中的代码复制到可执行文件里,而是在程序运行时动态地加载所需的库代码。这样可执行文件体积相对较小,而且多个程序可以共享同一个动态库,节省系统资源。比如在操作系统中,许多系统级别的功能模块是以动态库的形式存在,供不同应用程序调用。
动静态库的重要性
它们极大地提升了软件开发的效率和代码质量。通过复用已有的库代码,开发人员可以把更多精力放在业务逻辑的创新和完善上,同时减少了因重复编写代码可能导致的错误。而且在软件维护和升级方面,动静态库也提供了便利,只需要更新库本身,就能让依赖它的众多程序受益。
二、文件系统补充
上篇博客硬链接补充
补充1:当我们硬链接数被删除减到0时,文件就真正的被删除了。
补充2:操作系统会对内存进行管理,存在管理模块。
物理内存被分割成一个一个4KB小内存的(这个被称为页框),磁盘上的可执行文件等数据也被分割成了4KB的小文件(这个被称为页帧),页框和页帧之间互相交换数据,这个4KB就是数据交互单位
如果交互数据小了,那么两者直接的交互次数过多,io也就比较多,访问外设次数加多,时间效率就下来了;
比较大的时候,当我们想要读取少量的数据时,就会出现就会有很多的垃圾数据,哪些数据用不上就会占取内存空间。这就是预加载机制。
补充3:操作系统如何管理内存?
操作系统可以看到虚拟地址,也可以看到硬件的物理地址!
操作系统想要管理物理地址那么就要先描述在组织。
1024^3=1GB
4*1024^3=4GB
(4GB/1024)/4=1048576个页框
struct page men_array[1048576];
这就是框号,对数组管理就变成了对数组管理。
所以我们要访问一个内存,我们只需要先直接找到这个4KB内存的对应的page,就能在系统中找到这个内存对应的物理页框。
所以写时拷贝的时候也是4kb的大小写和拷贝。
所以申请内存空间的动作,都是对page的访问。
补充3:Linux中每个进程及打开文件具有自己的inode属性和文件页缓冲区(记结论,解释了解即可)
以下是结合驱动和文件从磁盘加载到内存的过程对Linux中每个进程及打开文件具有自己的inode属性和文件页缓冲区的解释:
1. 文件在磁盘的存储与inode
• 在Linux文件系统中,文件存储在磁盘上。每个文件都有对应的inode(索引节点),它包含了文件的元信息,比如文件的大小、权限、所有者、时间戳以及最重要的------指向文件数据块在磁盘上存储位置的指针等。
2. 进程打开文件的操作
• 当一个进程要打开一个文件时,它会通过系统调用(如open())来请求内核打开指定文件。
• 内核首先会根据文件路径,在文件系统中找到对应的inode。这个inode是整个文件系统层面共享的关于该文件的关键信息存储结构,但每个进程在打开这个文件时,会在内核中为自己建立一个独立的文件结构体(比如struct file),这个结构体里会包含指向该文件inode的指针等信息,也就相当于进程拥有了自己关联到这个文件inode的一个"入口",从而有了自己的inode属性相关的内容(虽然实际数据在共享的inode里,但通过这个结构体可以按进程需求来访问和操作)。
3. 文件从磁盘加载到内存(涉及驱动)
• 驱动初始化:在系统启动时,存储设备(如硬盘)的驱动程序会被加载和初始化。驱动程序负责与硬件设备进行通信,理解设备的特性和操作方式。
• 文件读取请求:当进程打开文件并尝试读取文件内容时(比如通过read()系统调用),内核会根据进程提供的文件描述符(在打开文件时返回给进程的一个标识)找到对应的文件结构体,进而找到文件的inode。
• 通过驱动读取磁盘数据:inode中包含了文件数据在磁盘上的存储位置信息,内核借助已经初始化好的磁盘驱动程序,向磁盘设备发出读取数据的请求。磁盘驱动程序会按照硬件的要求,将磁盘上对应的数据块读取到内存中的特定区域,这个区域通常就是文件页缓冲区。
4. 文件页缓冲区
• 页缓冲的概念:为了提高文件读取的效率,Linux采用了页缓冲机制。内存被划分成固定大小的页(通常是4KB),文件数据从磁盘读取到内存时,是以页为单位存放在文件页缓冲区中的。
• 进程与文件页缓冲区:每个进程打开文件时,内核会为其建立与该文件相关的文件页缓冲区管理机制。不同进程打开同一个文件时,虽然可能最终读取的数据是相同的,但它们在内存中有各自独立的页缓冲区(或者说对页缓冲区有各自独立的访问和管理方式)。这是因为每个进程可能在不同时刻、以不同的顺序读取文件的不同部分,有自己的读写指针位置等,所以需要各自独立的缓冲来保证正确的文件操作逻辑。
• 例如,进程A可能正在读取文件的开头部分,将对应的磁盘数据页加载到自己的文件页缓冲区中进行处理;而进程B可能同时在读取文件的中间部分,也会将相应磁盘数据页加载到它自己的文件页缓冲区,彼此互不干扰,并且都能根据自己的需求通过各自的文件结构体和inode信息准确地从磁盘获取并在内存中处理文件数据。
总之,在Linux中每个进程和其打开的每个文件有自己的inode属性相关设置便于按进程需求访问文件元信息,有自己的文件页缓冲区是为了高效且独立地在内存中处理从磁盘加载的文件数据。
以上文件系统全部结束
关于文件系统、文件以及block(数据块)相关内容的总结:
文件从磁盘打开到内存和加载到缓冲区的过程
文件系统
• 是用于管理和存储文件的一种机制,它组织磁盘等存储设备上的数据,使得用户和程序能够方便地访问、存储和操作文件。不同的操作系统通常有各自典型的文件系统,如Linux常用的ext4、Windows的NTFS等。
文件
• 文件是存储在文件系统中的基本数据单元,它可以包含各种类型的数据,如文本、图像、程序代码等。每个文件有特定的属性,像文件名、大小、创建时间、权限等,这些属性通常由文件系统进行管理和维护。
block(数据块)
• 是文件系统中存储数据的基本单位。文件在磁盘上存储时,会被分割成若干个数据块进行存放。例如,当一个文件较大时,它会占用多个数据块。数据块的大小在不同的文件系统中可能有差异,一般是固定值(如常见的4KB等)。
它们之间的关系
• 文件被存储在文件系统中是以数据块为单位进行分配空间的。当创建一个文件时,文件系统会根据文件的大小,为其分配相应数量的空数据块来存储文件内容。文件的元数据(如文件名、大小等属性)通常会通过文件系统中的inode(索引节点)等结构来管理,而inode也会记录文件数据块在磁盘上的存储位置信息,以便在访问文件时能准确找到对应的文件数据块并读取出来。
总之,文件系统通过对文件和数据块的合理组织与管理,实现了高效、有序地存储和访问文件的功能。
三、动静态库
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文 件的整个机器码。
在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking) 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。
操作系统采用虚 拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
当我们想把实现的提供给别人的方法:
1、将源代码提供给别人
2、给别人一个库
1、制作静态库
libXXX.a----静态链接
libYYY.so----动态库
自己设计静态库
创建头文件
cpp
#pragma once
#include <stdio.h>
extern int myerrno;
int add(int x, int y);
int sub(int x, int y);
int mul(int x, int y);
int div(int x, int y);
创建方法
cpp
#include "mymath.h"
int myerrno = 0;
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x * y;
}
int div(int x, int y)
{
if(y == 0){
myerrno = 1;
return -1;
}
return x / y;
}
创建完头函数和实现方法,揪下来就是打包封装成静态库
库(library)
静态库需要.o文件
在创建静态库之前先来了解打包形成静态库命令
创建静态库命令
ar -rc 打包文件名 被打包文件
ar是gnu归档工具,rc表示(replace and create)
查看静态库中的目录列表命令 ar -tv 打包文件名
t:列出静态库中的文件 v:verbose 详细信息
makefile
cpp
lib=libmymath.a//想形成一个库,名字叫libmath.a
$(lib):mymath.o//让源文件变成.o
ar -rc $@ $^//ar是生成静态库命令,并却打包形成.a
mymath.o:mymath.c
gcc -c $^//编译形成.o文件
.PHONY:clean
clean:
rm -rf *.o *.a lib
.PHONY:output
output:
mkdir -p lib/include//在libmymath.a中创建头文件
mkdir -p lib/mymathlib//在libmymath.a中创建实现方法文件
cp *.h lib/include//将头文件拷贝过去
cp *.a lib/mymathlib//将打包文件放过去
这就将文件创建好了。
我们将文件移到lib里面
这样我们的静态库就创建好了; 静态库就是lib。
将来像要给别人分享就直接分享lib即可;
2、库的使用
默认到系统的头文件当中去找头文件,但是在这里我们指定了路径,就会到指定路径去找。
但是还是报错,那么出错在那呢?编译还是链接呢?
我们发现我们加选项-c可以生成.o文件。那么就是链接错误。报错原因是因为找不到方法的实现。我们不知道实现在那里,我们在链接的时候找不到实现。
所以我们在编译时我们加上-L选项
指令 gcc main.c -L. -lmymath
-L 指定库路径 -l指定库名
-I指定头文件路径(这是大i)
这是指定了头文件;
没有头文件,像这样;
报错找不到都文件
还有一种方法就是在系统的头文件当中创建软链接(快捷指令)就可以了,或者将lib拷贝过去。
下一篇博客模拟实现和讲解动态库
总结
是用于管理和存储文件的一种机制,它组织磁盘等存储设备上的数据,使得用户和程序能够方便地访问、存储和操作文件。不同的操作系统通常有各自典型的文件系统,如Linux常用的ext4、Windows的NTFS等。
静态库的创建和使用