库的制作与原理, 软硬链接 动态库的运行 linux ─── 第20课

什么是库

        库是写好的现有的,成熟的,可以复⽤的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个⼈的代码都从零开始,因此库的存在意义⾮同寻常。 本质上来说库是⼀种可执行代码的⼆进制形式,可以被操作系统载入内存执行

库有两种:

  • 静态库 .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)

静态库的使用

测试目标文件生成后,静态库删掉,程序照样可以运行 

关于 -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

方法一: 将头文件和静态库添加到系统里

main.c函数内部

  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: 将库的路径放到环境变量表中

        我们前面学习过, l操作系统启动时 , bash 的环境变量是从磁盘文件中加载进来的,我们此时是将库的路径加载到环境变量中 ,没有加载到磁盘文件,下一次开机 ,此库就又找不到了

我们可以将其直接写到磁盘文件中

(此库非常重要 ,想让它在任何地方都能用  ,用方法4)方法4: 写配置文件 

        系统也有一个全局的配置路径 ,我们可以写一个配置文件( .conf结尾) 将库路径写入 ,系统自己就能找到这个库了

动态库与静态库

同时提供给gcc /g++提供了( .a)静态库和( .so)动态库 ,gcc/g++ 默认使用动态库

动态库工作原理(共享库)

进程将动态库经过虚拟地址映射到进程的虚拟地址空间(共享区)       (后面学虚拟地址)

进程如何使用动态库

        ./main 执行 ,将mian的代码和数据加载到物理内存 ,将动态库也加载到物理内存,库(共享区)和程序的代码(代码区)及数据(数据区)都映射在进程的虚拟地址空间中.

         进程拿到了main的代码和数据,运行代码区时,遇到函数实现 (函数的实现在动态库里) , 跳转到虚拟地址空间中(共享区)执行它的虚拟地址对应的方法 ,执行完跳回代码区继续向下执行.

不同进程使用同一个动态库的情况

不同进程使用同一个动态库 ,不需要重新加载库 ,只需要映射到新进程的页表即可

下一个博客讲解 :

1.动态库加载到物理内存的哪个位置? 

2.映射到虚拟地址空间的哪个位置?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一码归—码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值