库的制作与原理 linux第课
什么是库
库是写好的现有的,成熟的,可以复⽤的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个⼈的代码都从零开始,因此库的存在意义⾮同寻常。 本质上来说库是⼀种可执⾏代码的⼆进制形式,可以被操作系统载⼊内存执行。
库有两种:
- 静态库 .a [Linux]、.lib [windows]
- 动态库 .so [Linux]、.dll [windows]
// Centos 动静态库
// C
$ ls /lib64/libc-2.17.so -l
-rwxr-xr-x 1 root root 2156592 Jun 4 23:05 /lib64/libc-2.17.so
$ ls /lib64/libc.a -l
-rw-r--r-- 1 root root 5105516 Jun 4 23:05 /lib64/libc.a// C++
$ ls /lib64/libstdc++.so.6 -l
lrwxrwxrwx 1 root root 19 Sep 18 20:59 /lib64/libstdc++.so.6 ->
libstdc++.so.6.0.19
$ ls /usr/lib/gcc/x86_64-redhat-linux/4.8.2/libstdc++.a -l
-rw-r--r-- 1 root root 2932366 Sep 30 2020 /usr/lib/gcc/x86_64-redhatlinux/4.8.2/libstdc++.a
软硬链接
创建软链接
- 通过文件的inode号不同 ,可以看出建立软链接 ,本质是创建了一个独立文件(软链接有自己的inode号)
- 软链接本身只是一个指向目标文件或目录的快捷方式,访问软链接时,系统会自动重定向到目标文件或目录。用户使用目标文件的软链接 系统会根据软链接中的路径找到目标文件 软链接类似与windows下的桌面快捷图标
- 删除链接的命令 unlink test-soft.link
创建硬链接
- 硬链接不是新文件 ,没有自己的inode号 ,就是多了一组文件名与已存在文件的映射关系
- 多一个文件名与相同的文件(inode)进行映射 ,inode本身的引用计数加一 ,inode自身的引用计数就是硬连接数,只有最后减到0时才会关闭文件
软链接的应用
软链接诶就是一种快捷方式 ,可以快速定位文件或者 指令
软链接也可以链接目录
硬连接的应用
- 创建一个空目录,此时空目录名和其目录下的 . 都与空目录的inode产生了映射 ,此时此目录的硬连接数为2
- 如果在空目录中创建新目录 , 新目录中会有 .. 是对上级目录的硬连接 ,上级目录的硬链接数变为3
- 在linux 中对文件备份,只需要硬链接此文件即可 ,利用了inode的引用计数.
注意:
linux不允许用户对目录进行硬链接 (会形成环状结构) , 路径查找就是利用硬连接
(虽然linux自己用了 . 和..对目录进行了硬连接)
可以对目录进行软链接
静态库
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再需要静态库。
- ⼀个可执行程序可能用到许多的库,这些库运行有的是静态库,有的是动态库,而我们的编译默认为动态链接库,只有在该库下找不到动态.so的时候才会采用同名静态库。我们也可以使⽤gcc的 -static 强转设置链接静态库。
前提: 我们静态库在lesson21中生成 ,main.c在lesson22中
创建静态库
库的开头是lib ,静态库结尾是.a ,动态库的结尾是.so
将.o文件打包,形成静态库 使用 ar -rc命令
ar -rc libmystdio.a my_stdio.o my_string.o
ar 是 gnu 归档工具, rc 表示(replace and create)
静态库的使用
第一种将头文件和静态库添加到系统里
main.c函数内部
测试目标文件生成后,静态库删掉,程序照样可以运行
关于 -static 选项,稍后介绍
库文件名称和引入库的名称:去掉前缀 lib ,去掉后缀 .so ,.a ,如: libc.so -> c
- 场景1:头⽂件和库⽂件安装到系统路径下 $ gcc main.c -lmystdio
- 场景2:头⽂件和库⽂件和我们自己的源文件在同⼀个路径下 $ gcc main.c -L. -lmymath
- 场景3:头⽂件和库⽂件有自己的独⽴路径 $ gcc main.c -I(大写I)头文件路径 -L库文件路径 -lmymath
1 #include <my_stdio.h> //因为第一种将库加载到系统中#include <my_string.h>//所以用<my_stdio.h> 而不是"my_stdio.h"#include <stdio.h>4 int main ()5 {6 const char * s="hello linux";7 printf("%s:%d\n" ,s, my_strlen(s));8 9 mFILE *fp= mfopen("./log.txt", "a");10 if(fp == NULL) return 1;11 mfwrite(s , my_strlen(s) ,fp);12 mfwrite(s , my_strlen(s) ,fp);13 mfwrite(s , my_strlen(s) ,fp);14 mfwrite(s , my_strlen(s) ,fp);15 mfclose(fp);16 return 0;18 }
从系统中删除我们写的静态库和头文件
第二种不将静态库安装到系统里面
直接将头文件和静态库交给其他用户与源文件一起使用
-L : 指定库路径
-l (小写l): 指定库名
第三种使用带路径的库
- 库中都不能包含main函数
- 库发布时 ,建议将头文件都放在include目录下
- 建议将库文件放在 lib目录下
动态库
- 动态库(.so):程序在运行的时候(不是gcc)才去链接动态库的代码,多个程序共享使用库的代码。
- ⼀个与动态库链接的可执行文件仅仅包含它⽤到的函数⼊⼝地址的⼀个表,而不是外部函数所在目标文件的整个机器码
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中, 这个过程称为动态链接(dynamic linking)
- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采 用虚拟内存机制允许物理内存中的⼀份动态库被要用到该库的所有进程共用,节省了内存和磁盘空 间
创建动态库
使用gcc -o $@ $^ -shared
使用动态库
第一种方法
删除动态库 原来用到此动态库的可执行程序就不能运行了
第二种方法
第三种方法
makefile代码
我们发现我们在生成可执行程序时 ,指明了头文件的位置 ,库文件的位置 ,库文件的名称 ,成功生成了可执行文件 ,但是无法运行
原因是 我们只是在生成可执行时,告知了gcc 头文件库文件位置,但是gcc已经完成了编译
运行时,程序还不知道动态库头文件在哪(静态库的可执行程序不用找)
解决方法
方法1: 向第一种一样将 头文件和动态库加入到系统中
方法2: 在系统路径建立软链接
方法3:
方法4: