文章目录
目录
前言
在上一篇博客中,已经解释动静态库的概念
在上一篇博客中已经创建了一个静态库,并且使用了一下静态库。
库搜索路径
从左到右搜索-L指定的目录。
由环境变量指定的目录 (LIBRARY_PATH)
由系统指定的目录
/usr/lib
/usr/local/lib
/lib64/
1、第三方库,在往后使用时,必须要用gcc -l 来指定文件名;
2、深度理解errno本质;
3、查看动静态库链接
ldd a.out
4、gcc 默认是动态库链接,如果没有动态库,只能用静态库链接;
5、如果需要链接多个库,gcc可以链接多个库;
6、当我们把头文件加载到了系统文件当中,就不用再指明路径了。但是需要加-l指明文件名;
7、软硬链接 ln -s 目标路径
一、静态库使用
静态库使用方法:
1、指令 gcc main.c -I 指定头文件路径 -L 指定方法路径 -l方法文件名
这个方法在上篇博客已经介绍过了。
2、将目标路径加载到系统文件加当中,也就是将静态库mv或者cp当系统文件当中;
将头文件拷贝到/usr/include当中
将静态库拷贝到/lib64/
3、添加软链接,将目标文件软链接到系统文件当中。
为什么还是报错呢?因为我们main函数中包含的是没有math.h这个文件,而我们链接文件名为myinc,main函数中也就需要更改;
以上就是静态库的所有用法
二、动态库
1.创建动态库
创建mylog.c mylog.h myprint.c myprint.h
mylog.c
#include "mylog.h"
void Log(const char*info)
{
printf("Warning: %s\n", info);
}
mylog.h
#pragma once
#include <stdio.h>
void Log(const char*);
myprint.c
#include "myprint.h"
void Print()
{
printf("hello new world!\n");
printf("hello new world!\n");
printf("hello new world!\n");
printf("hello new world!\n");
printf("hello new world!\n");
}
myprint.h
#pragma once
#include <stdio.h>
void Print();
创建好了进行打包
无论是动态库还是静态库,我们都要的是.o文件
用.o文件进行链接
动态库生成.o命令:
gcc -fPIC -c 要编译文件
fPIC:产生位置无关码(position independent code)
这里我们将动态库和静态库分开,那么mymath.o是用不上的。rm 掉。
打包:
方法一:命令行方式
打包命令
gcc -shared -o 目标文件名.so *o
shared: 表示生成共享库格式
gcc提供了动态库打包指令
静态库需要借助第三方工具 ar
这个文件是跑不了的,它连main函数都没有。
静态库再别人使用时是不需要加载到内存的,当编译时,编译器将静态库以二进制的方式拷贝过来。
动态库再使用时,是需要加载的,需要加载到内存,和我们的程序形成关联,我们要用动态库的函数时,会跳转的动态库当中。所有我们的libmymethod.so是带有x权限的;
方法二:makefile
科普一下makefile,大家可以找一篇博客看一下。
"="是最普通的等号,在Makefile中容易搞错赋值等号,使用 “=”进行赋值,变量的值是整个Makefile中最后被指定的值。
$符号表示取变量的值,当变量名多于一个字符时,使用"( )"
$符的其他用法
$^ 表示所有的依赖文件
$@ 表示生成的目标文件
$< 代表第一个依赖文件
伪目标 .PHONY
伪目标只是一个标签,clean是个伪目标没有依赖文件,只有用make来调用时才会执行
当目录下有与make 命令 同名的文件时 执行make 命令就会出现错误。
通常也会把ALL设置成伪目标
dy-lib=libmymethod.so
static-lib=libmymath.a
.PHONY:all
all: $(dy-lib) $(static-lib)
$(static-lib):mymath.o
ar -rc $@ $^
mymath.o:mymath.c
gcc -c $^
$(dy-lib):mylog.o myprint.o
gcc -shared -o $@ $^
mylog.o:mylog.c
gcc -fPIC -c $^
myprint.o:myprint.c
gcc -fPIC -c $^
.PHONY:clean
clean:
rm -rf *.o *.a *.so mylib
.PHONY:output
output:
mkdir -p mylib/include
mkdir -p mylib/lib
cp *.h mylib/include
cp *.a mylib/lib
cp *.so mylib/lib
make:
make output
2.使用
我们调用静态库还是可以的。
那么我们将动态库函数放开;
我们尝试使用静态库的调用方法来调用动态库。
可以形成可执行文件但是跑不起来;发现报错了。
解释:
编译器是知道我们动态库在哪里了,但是动态库是加载到文件当中,在执行文件是遇到对应函数跳转过去的,所以编译时是没有文件了。但是运行文件时,系统不知道动态库是在那里的。
为什么c库的时候没有问题?可以加载呢?
因为c库在加载时有自己的路径,它加载了环境环境变量当中。
接下来我们研究动态库加载数据!!!
3、解决动态库的加载问题
1、直接将动态库拷贝到lib64当中,和静态库一样。(感兴趣看上面的静态库拷贝过程自己玩一玩)
2、在lib64当中建立一个软链接(和静态库一样)
查看路径
建立软链接
我们发现不用编译加载器都可以找到;
执行一下发现没有问题
3、将动态库加载到环境变量当中
和环境变量相关的命令
1.env: 显示所有环境变量。
2.printenv: 打印指定的环境变量。
3 export: 设置或显示环境变量。
4.unset: 删除环境变量。
5.echo: 输出环境变量的值。
6.set:显示本地定义的shell变量和环境变量
查看这个环境变量指令
echo $LD_LIBRARY_PATH
这个就是曾经加载的变量。
我们发现我们不需要将这个动态库的文件名加载进去,他会自己找,加载了就还是会找不到的。
我们知道我们的环境变量在退出后环境变量就没有了,那么再次打开时就又找不到了。
重新启动后:
我们想要不打开还在,我们就将环境变量加载到~/.bash_profile这个启动环境变量当中
借助当初的环境变量在看一下
linux进程的状态之环境变量_进程启动后 环境变量-CSDN博客
在这里加入这个环境变量即可;
4、ldconfig 配置/etc/ld.so.conf.d/,建立自己的动态库路径配置文件,然后ldconfig更新
随便打开一个文件
我们发现这都是一个路径。
我们在这个路径下面创建一个 .conf的文件
在文件中把动态库文件路径放在这个里面
创建完成以后再将文件重新加载(更新)一下
ldconfig更新
然后这里就又有了。
当我们很多库在同一个路径当中时,我们不用把每一个路径都创建一个.conf文件,我们加它们放在一个文件当中就可以了,然后分别把每个路径列一下。
练习:
安装ncurses ---基于终端的图形界面库
三、共享库
动态库在进程运行时是需要加载的,静态库不需要
当我们形成可执行文件了,我们把静态文件删除
常见的动态库被所以的可执行文件(动态链接的),都要使用这个动态库,所以动态库也叫做共享库。
所以动态库在系统加载以后,会被所有进程共享。加载到了内存当中也就只用一份,大大节省了空间。
当我们用十几二十个动态库,它们都会被加载到内存,然后操作系统对这些库先描述在组织,形成链表,管理时就是对链表的增删查改。
动态库是怎么做到对所有进程进行共享的?
每个进程都有自己的pcb
事实上:系统在运行中,一定会存在多个动态库,--os管理,系统将这些进程还有动态库分别先描述,在组织串在一起;
所有系统对所有库的加载情况还有进程都是非常清楚的。
当进程共享同一个动态库时,我们的cpu调度进程的pcb,拿到pcb以后,到地址空间中去找代
码,代码是数据,数据存储在磁盘当中的;就会到物理内存当中去找代码,发现代码没有从磁盘加载过来,那么就会去找inode编号,那到inode编号然后到inode bitmap当中去看是否有数据,有数据通过inode编号在inode block当中拿到block编号,然后到对应的块当中去读取数据,一次读取4kb大小内存,也就是8个扇区的数据,将这些数据加载到物理内存当中,然后再通过页表映射到地址空间的代码区;
如果是共享块数据,那么将数据加载当地址空间的共享区,当调用代码时,就会到共享区去找代码,就做到执行任何代码都是再进程的地址空间中执行的;
总结:进程和进程都是独立的;建立映射,从此以后,我们执行的任何代码,都是在我们进程地址空间中执行的。
当动态库当中有全局变量,当我们一个进程将这个全局变量给修改了,但是我们另一个进程去访问时,是不会被修改的,这是因为我们的数据和页表之间存在写时拷贝,它们只会将共享区的数据分别拷贝一份,互相之间都是独立的
总结
动态库在进程运行时是需要加载的,静态库不需要
当我们形成可执行文件了,我们把静态文件删除
常见的动态库被所以的可执行文件(动态链接的),都要使用这个动态库,所以动态库也叫做共享库。
所以动态库在系统加载以后,会被所有进程共享。加载到了内存当中也就只用一份,大大节省了空间。
动态库的创建和使用
理解动态库的加载