Bootstrap

linux——动静态库

Linux——文件系统清尾、动静态库-CSDN博客


文章目录

目录

文章目录

前言

一、静态库使用

静态库使用方法:

二、动态库

1.创建动态库

 打包:

伪目标 .PHONY

​编辑

2.使用

3、解决动态库的加载问题

三、共享库

动态库是怎么做到对所有进程进行共享的?


前言

在上一篇博客中,已经解释动静态库的概念

在上一篇博客中已经创建了一个静态库,并且使用了一下静态库。

库搜索路径

从左到右搜索-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个扇区的数据,将这些数据加载到物理内存当中,然后再通过页表映射到地址空间的代码区;

如果是共享块数据,那么将数据加载当地址空间的共享区,当调用代码时,就会到共享区去找代码,就做到执行任何代码都是再进程的地址空间中执行的;

总结:进程和进程都是独立的;建立映射,从此以后,我们执行的任何代码,都是在我们进程地址空间中执行的。

当动态库当中有全局变量,当我们一个进程将这个全局变量给修改了,但是我们另一个进程去访问时,是不会被修改的,这是因为我们的数据和页表之间存在写时拷贝,它们只会将共享区的数据分别拷贝一份,互相之间都是独立的


 总结

动态库在进程运行时是需要加载的,静态库不需要

当我们形成可执行文件了,我们把静态文件删除

常见的动态库被所以的可执行文件(动态链接的),都要使用这个动态库,所以动态库也叫做共享库。

所以动态库在系统加载以后,会被所有进程共享。加载到了内存当中也就只用一份,大大节省了空间。

动态库的创建和使用

理解动态库的加载

;