独立文件:软链接文件在文件系统中是一个独立文件,拥有自己的inode编号和属性。
动态库版本管理:对于共享库,可以使用软链接来管理不同版本动态库之间的切换。如:创建一个指向最新版本动态库的软链接,当库升级时,仅需要更新软链接指向新版本的动态库。
建立硬链接,并没有新建文件,而是为文件创建了新的文件名,硬链接不是一个独立的文件,因为inode与目标文件的inode相同。
ln 源路径 硬链接路径
一、硬链接计数的概念
二、硬链接计数的变化场景
创建文件:当创建一个新文件时,这个文件的硬件计数默认为1,因为此时只要一个文件名指向这个inode。
创建硬链接:当使用ln创建一个文件的硬链接,不会创建一个新的文件,只会为该文件创建一个新的文件名,并增加inode中的硬链接计数。
删除文件:当删除一个文件时,会删除指向该文件的文件名,并减少inode中的硬链接计数,如果此时硬链接计数等于0,文件才会被删除,系统回收该inode及其所占的磁盘空间。
三、硬链接计数的限制
不能跨文件系统:硬链接只能在同一文件系统内创建,不能跨文件系统或分区,因为inode只在分区内唯一。
共享inode:硬链接与原始文件共享同一个inode编号,意味着它们指向同一个物理文件的数据块,对物理文件所做的任何更改都将反映在所有硬链接上。
增加访问路径:创建硬链接实际上是为文件增加了一个新的访问路径或文件名。
无差别访问:无论通过哪个文件名访问文件,都指向同一个inode,即:指向同一个文件内容。因此硬链接与原始文件无差别,二者等价。
节省空间:由于硬链接共享相同的inode和数据块,因此它们不会占用额外都磁盘空间。
删除与移动:如果删除目标文件或硬链接,只要inode中的硬链接计数不为0,文件仍可以被硬链接访问,直到硬链接计数=0,文件才被真正删除。
硬链接应用场景如下:数据备份、数据共享、数据恢复、版本控制、自动同步等。
如果我希望其他人能够使用我编写的方法,通常的方法是提供一组同名方法.o文件(预编译的对象文件),然后将所有.o文件打包形成一个库文件,和相应的同名方法.h头文件。即:库文件 + 头文件。
库底层封装了重复的代码和常用的功能,使得开发者在编写项目时可以直接使用这些现成的代码,而无需从头开始编写,这大大减少了开发的时间,提高了开发效率。
这意味着用户只能看到库提供的接口(通过头文件.h),而无法直接访问或修改库中的源代码,这有助于保护开发者的知识产权和代码实现。
#include"my_add.h"
int my_add(int a, int b)
{
return a + b;
}
#include"my_sub.h"
int my_sub(int a, int b)
{
return a - b;
}
ar [options] archive-file object-files
archive-file是静态库名(lib库名.a);object-files是要添加到库中的对象名(.o文件)。
常见的选项
-c:创建库文件,如果库已存在,则会被覆盖。
-r:向库文件中添加.o文件,如果.o文件已在库中存在,则会被替换。
-t:列出库文件中包含的.o文件列表。
-v:在执行过程中显示详细的信息。
第一步:编译形成 .o 文件。
第二步:使用ar命令,将所有.o文件进行打包,形成静态库文件。
第三步:将库进行标准化。
libmyc.a:my_add.o my_sub.o //第二步:使用ar命令,将所有.o文件进行打包,形成静态库文件
ar -rc $@ $^
%.o:%.c //第一步:编译形成 .o 文件
gcc -c $<
.PHONY:clean
clean:
rm -rf *.a *.o mylib mylib.tgz
.PHONY:output //第三步:将库进行标准化
output:
mkdir -p mylib/include
mkdir -p mylib/lib
cp -rf *.h mylib/include/
cp -rf *.a mylib/lib/
tar czf mylib.tgz mylib
#include"my_add.h"
#include"my_sub.h"
int main()
{
int a = 4, b = 3;
printf("%d + %d = %d\n", a, b, my_add(a, b));
printf("%d - %d = %d\n", a, b , my_sub(a, b));
return 0;
}
当链接器在链接中需要找到某个库文件(.so、.a),它先会在标准的位置(系统默认的库路径)中查找,如果查找不到,链接器就会使用-L指定的路径进行搜索。
这些选项分别用于控制编译和链接过程中的头文件、库文件的搜索路径和库文件的选择。
第一步:使用-fPIC选项,编译形成 .o 文件。
第二步:使用-shared,将所有.o文件进行打包,形成动态库文件。
第三步:将库进行标准化。
libmyc.so:my_add.o my_sub.o //第二步:使用-shared,将所有.o文件进行打包,形成动态库文件
gcc -shared -o $@ $^
%.o:%.c //第一步:使用-fPIC选项,编译形成 .o 文件
gcc -c -fPIC $<
.PHONY:clean
clean:
rm -rf *.so *.o mylib mylib.tgz
.PHONY:output //第三步:将库进行标准化
output:
mkdir -p mylib/include
mkdir -p mylib/lib
cp -rf *.h mylib/include/
cp -rf *.so mylib/lib/
tar czf mylib.tgz mylib
现象:程序运行时,OS加载动态库文件myc失败,即:系统找不到这个文件。
将库或其他软件安装到系统中,本质是把对应的文件,拷贝到系统默认的搜索路径中。
在64位系统,系统中库默认的搜索路径为/lib64、/usr/lib64;在32系统,系统中库默认的搜索路径为/lib、/usr/lib。
可执行程序只能进行静态链接,指的是对于特定库的链接方式: 当系统中只存在某个库的静态版本,任何尝试链接到这个库的程序都必须使用使用静态链接,因为动态链接器在运行时无法找到这个库的动态版本。
程序不一定整体是静态链接的,取决于它所依赖的所有库,以及编译链接时的选项: 一个程序可能依赖于多个库,此外,即使程序使用了静态链接来链接某些库时,它仍可能依赖于动态链接的库(C、C++标准库或系统库),这些库在OS级别上提供,且以动态链接的形式存在,以便在多个程序之间共享。
一、动态库概念
与静态库不同,静态库在程序编译时会被完全复制到可执行文件中,而共享库则在程序运行时被加载到内存中,如果多个程序使用同一个共享库,OS会让这些进程共享内存中的同一份库代码和数据,即:动态库的代码和数据在内存中只存在一份。
二、动态库加载的过程
检查依赖:程序启动时,动态链接器会检查该程序依赖的所有动态库。
搜索路径:动态链接器会在预设的库搜索路径中查找所需的动态库文件。
加载与映射:第一次加载、后续加载。
可编址的范围:32位平台,[0, 2^32] -> [0, 4GB] ,64位平台,[0, 2^64] -> [0, 16GB]。
在计算机早期,程序使用绝对编址,因为当时系统比较简单,程序通常使用直接映射到物理内存的绝对编址,而没有复杂的内存管理和保护机制,如:虚拟内存、页表等。
现代计算机系统广泛使用相对编址,因为这提供了更好的灵活性和安全性。
一、一般程序加载的过程
读取可执行文件,并解析文件中各个段的信息:OS先从存储介质(如硬盘)中读取可执行文件(程序代码、数据以及依赖的库),然后OS会解析可执行文件中的各个段(如代码段、数据段、堆栈段等)的信息。这些段包含了程序运行所需的所有指令和数据。
初始化数据段和未初始化数据段、构建页表。
三、CPU执行程序的过程