Linux 动静态库的制作,使用和加载

Linux 动静态库的制作,使用和加载

  • 一.前置说明
    • 1.mylib.h
    • 2.mylib.c
    • 3.mymath.h mymath.c
    • 4.如何制作库
  • 二.动静态库的制作
    • 1.静态库的制作
      • 1.制作
      • 2.使用一下静态库,验证是否成功打包
    • 2.动态库的制作
      • 1.编译.c源文件文件生成.o目标文件
      • 2.打包生成动态库
      • 3.编写makefile文件,自动化制作动态库
      • 4.使用一下动态库,验证是否成功打包
    • 3.动态库的打包与发布
  • 三.动态库的使用
    • 1.下载压缩包并解压
    • 2.使用动态库对main.c进行编译
    • 1.安装动态库到系统当中
      • 演示
      • 验证
    • 2.修改环境变量LD_LIBRARY_PATH
      • 1.演示+验证
      • 2.修改配置文件+验证
    • 3.在/lib64/目录下面建立动态库的软链接文件
      • 1.演示
      • 2.验证
    • 4.在/etc/ld.so.conf.d目录下添加.conf配置文件
      • 1.演示
      • 2.验证
    • 小小建议
  • 四.动静态库与动静态链接
    • 1.先上结论
    • 2.验证
      • 1.修改makefile
      • 2.同时提供动态库和静态库,gcc默认使用的是动态库
      • 3.只提供静态库,不提供动态库
        • 一个小问题
      • 4.同时提供动态库和静态库,采用静态链接进行编译
      • 5.只提供动态库还非要进行静态链接,那么就会发生链接报错
  • 五.理解动态库的加载
    • 1.站在系统的角度来进行理解
    • 2.补充一下关于可执行程序的知识点
      • 1.先说理论
      • 1.验证可执行程序当中代码指令本身就有地址
      • 2.Linux下逻辑地址等于虚拟地址
      • 3.可执行程序的表头及其区域划分
      • 4.CPU当中的程序计数器(pc指针/eip寄存器)
      • 5.理顺可执行程序的执行过程
        • 1.编译器进行编译生成可执行程序
        • 2.OS根据可执行程序的信息创建进程PCB
        • 3.CPU配合pc指针执行可执行程序的指令
      • 6.重新理解进程地址空间
      • 7.理解静态库的静态链接和加载问题
    • 3.理解动态库动态链接和加载问题
      • 1.理论
      • 2.结论

关于动静态库的初步理解,我们在Linux基础环境开发工具的使用(二):动静态库的理解,make,makefile
这篇博客中进行了详细介绍,大家可以去看一下

今天我们直接介绍动静态库的制作和使用,分别站在库的制作者和使用者的角度来分析
而我们要使用的文件就是我们在
Linux文件系列: 深入理解缓冲区和C标准库的简单模拟实现
这篇博客当中写的mylib.h mylib.c这几个文件

一.前置说明

1.mylib.h

#pragma once
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>#define SIZE 4096
#define DFL_MODE 0666#define FLUSH_NONE 1
#define FLUSH_LINE (1<<1)
#define FLUSH_ALL (1<<2)typedef struct my_file
{int fileno;char buffer[SIZE];int end;//缓冲区中有效数据的个数(也就是最后一个有效数据的下一个位置)int flag;//缓冲区的刷新策略
}my_file;my_file* myfopen(const char* path,const char* mode);int myfwrite(const char* s,int num,my_file* stream);int myfflush(my_file* fp);int myfclose(my_file* fp);

2.mylib.c

#include "mylib.h"my_file* myfopen(const char* path,const char* mode)
{int fd=0;int flag=0;if(strcmp(mode,"r")==0){flag |= O_RDONLY;}else if(strcmp(mode,"w")==0){flag |= (O_WRONLY | O_CREAT | O_TRUNC);}else if(strcmp(mode,"a")==0){flag |= (O_WRONLY | O_CREAT | O_APPEND);}if(flag & O_CREAT){fd=open(path,flag,DFL_MODE);}else{fd=open(path,flag);}//打开文件失败,设置errno错误码并返回NULLif(fd==-1){errno=2;return NULL;}//创建文件,设置fp的相应属性my_file* fp=(my_file*)malloc(sizeof(my_file));if(fp==NULL){errno=3;return NULL;}fp->fileno=fd;fp->flag=FLUSH_LINE;fp->end=0;return fp;
}//把s中的数据写入stream中
int myfwrite(const char* s,int num,my_file* stream)
{//保存旧的缓冲区的大小int pos=stream->end;//1.先写入用户级缓冲区memcpy(stream->buffer+pos,s,num);stream->end += num;//更新缓冲区大小//刷新策略:按行刷新if(stream->flag & FLUSH_LINE){//2.判断是否需要刷新缓冲区(判断是否有'\n')int flushit=0;while(pos < stream->end){if((stream->buffer[pos])=='\n'){flushit=1;break;}pos++;}if(flushit == 1){//3.刷新缓冲区:[0,pos]数据write(stream->fileno,stream->buffer,pos+1);//4.更新缓冲区 把[pos+1,count)的数据移动到[0,count-pos-2]当中//一共移动count-pos-1个数据//先求出要移动的最后一个数据的下标int count=stream->end;memmove(stream->buffer,stream->buffer+pos+1,count-pos-1);stream->buffer[count-pos-1]='\0';stream->end=count-pos-1;}}return num;
}int myfflush(my_file* fp)
{if(fp->end > 0){write(fp->fileno,fp->buffer,fp->end);fp->end=0;}return 0;
}int myfclose(my_file* fp)
{myfflush(fp);return close(fp->fileno);
}

3.mymath.h mymath.c

跟下面的两个文件mymath.h mymath.c
在这里插入图片描述

4.如何制作库

我们知道,在文件的编译链接时,只对每个.c文件进行单独编译形成.o目标文件,在链接阶段才会将所有的.o目标文件形成可执行程序

.h文件是不参与编译的,因为会在预处理阶段在包含该.h文件的文件当中展开

因此我们只需要将所有的.o目标文件用特定的方式进行打包,就能形成一个库文件

只要一个源代码需要用到某个库,只需要包含对应的头文件,并且拥有对应的库文件,就能够使用这个库文件当中的方法了

这样做的好处(也就是为什么要有库):
1.提高开发效率
2.隐藏源代码

二.动静态库的制作

1.静态库的制作

1.制作

在这里插入图片描述
这个c是create,表示如果当前目录中不存在这个库就创建这个库
这个r是replace,表示如果当前目录中存在这个库,就替换这个库(用于库的更新)
在这里插入图片描述

2.使用一下静态库,验证是否成功打包

下面我们把libmyc.a和.h头文件拷贝到user目录下,
然后写一个main.c
在这里插入图片描述
main.c里面成功使用了库当中的myfopen,myfwrite,myfclose,Add
函数

调用自定义库对文件进行编译,生成可执行程序
gcc main.c -l库名 -L 库所在的路径

在这里插入图片描述
在这里插入图片描述
成功编译,生成的可执行程序的执行情况正确

下面我们来制作一下动态库

2.动态库的制作

我们先删掉刚才生成的.o目标文件

1.编译.c源文件文件生成.o目标文件

我们知道gcc -c默认生成同名的.o文件
对于静态库而言,编译.c文件生成.o文件时直接使用gcc -c即可
而对于动态库来说,需要加上一个选项-fPIC(用于产生位置无关码)

gcc -fPIC -c mymath.c
gcc -fPIC -c mylib.c

在这里插入图片描述

2.打包生成动态库

对于静态库的打包,需要使用ar命令
但是对于动态库的打包,直接使用gcc即可
不过要加上-shared(表示生成共享库格式)选项

gcc -shared -o -libmyc.so(动态库名称lib前缀+库名称+.so后缀) mymath.o mylib.o 

在这里插入图片描述
此时我们的动态库就制作完成了

3.编写makefile文件,自动化制作动态库

下面我们写一个makefile文件,来自动化制作动态库
在这里插入图片描述
在这里插入图片描述

4.使用一下动态库,验证是否成功打包

跟静态库的验证一样,我们依旧是把.h头文件和动态库拷贝到user目录下
在这里插入图片描述
然后

gcc main.c -lmyc -L .
gcc main.c -l(要链接的库的名称) -L(要链接的库的路径)

因为我们制作的库属于第三方库,而gcc只认识c/c++的库和路径
因此必须要加上-l指定我们要额外链接的库名称
又因为gcc只会到系统默认的库搜索路径下进行搜索
并不会在当前路径下搜索库,因此我们需要加上-L指定要额外链接的库路径

指定完之后就等于告诉gcc,链接的时候不要忘了到xx路径下帮我链接一下xxx文件哦

在这里插入图片描述

3.动态库的打包与发布

刚才我们演示完了动态库的制作和使用的过程
我们也知道了对于编写库的人而言
我们要给用库的人提供的是头文件+库文件

刚才我们把头文件和动态库拷贝到当前路径下
可是如果我们今天要用的头文件和动态库有成百上千个怎么办呢?
全拷贝到当前路径下?

那未免也太挫了吧

因此下面我们来学习一下动态库的打包与发布

我们将其写入makefile当中,自动化完成其过程
在这里插入图片描述
生成伪目标文件:

make 伪目标文件名
因此只需要make output就能够完成动态库的打包与发布

下面我们测试一下
在这里插入图片描述
测试成功,下面我们重新make 和 make output
然后切换为库的使用者来用一下这个库

三.动态库的使用

1.下载压缩包并解压

czf:形成压缩包
xzf:解压
tar -czf/cvzf dst.tgz src
tar -xzf/xvzf dst.tgzdst:要形成的压缩文件的名称
src:要打包的文件名/目录名

在这里插入图片描述

2.使用动态库对main.c进行编译

此时头文件不在当前目录下,因此我们一定要加上某个选项来告诉gcc我们的头文件在哪里

这个选项就是-I选项(大写的i,不是小写的L)

gcc -o mytest main.c -L ./mylib/lib -lmyc -I ./mylib/include

在这里插入图片描述
成功生成mytest这个可执行程序
下面我们运行这个可执行程序
在这里插入图片描述
报错了,找不到这个库,为什么呢?
我刚才不是在gcc的时候用-L选项告诉你这个动态库在哪里了吗?
为什么你还跟我说你找不到呢?

我们要理清一个思路
1.我们刚才是告诉gcc这个路径的
2.gcc只负责编译链接生成可执行程序,不负责执行可执行程序
3.可执行程序的执行是由OS负责的,跟gcc无关
4.我们并没有告诉OS我们的动态库在哪里

此时我们就能理解为什么这里会报错了

那么我们应该怎么解决这个问题呢
有4种解决方法,下面我们一一介绍,大家可以随意挑选使用

1.安装动态库到系统当中

想一下,我们平常执行可执行程序的时候可是从来都没有出现过这种问题的哦
为什么呢?
因为我们平常用的库都是c/c++的官方库,OS默认就回到对应的路径当中去查找这个库,因此不存在找不到的问题
而且gcc,g++也是默认到这个路径当中去查找的

因此我们只需要将libmyc.so这个动态库安装(拷贝)到系统的那个路径当中,OS不就能找到了吗,而且gcc/g++也能顺带找到哦

因此:

所谓的把库(或者其他软件)安装到系统当中,本质上就是把对应的文件拷贝到系统指定的路径当中!

注意:这个路径是机器指定好的,一般都是root的目录
因此拷贝的时候需要使用sudo命令或者切换成root身份来操作

演示

因为要用sudo修改/lib64目录下的内容,因此这个过程的操作一定要小心
千万不能误操作…后果你懂的…

这个路径就是根目录当中的lib64这个目录
在这里插入图片描述
后面还有一堆,就不放进来了,大家知道这里有一大堆动静态库就行了
在这里插入图片描述

验证

此时我们不仅能够执行这个可执行程序了
而且gcc的时候我们也不用指明库的路径了
但是库的名称依然要指定(因为我们用的是第三方库,gcc/g++不认识)
在这里插入图片描述
运行成功
下面为了演示后面的操作,我们先把libmyc.so从/lib64/目录下移除
在这里插入图片描述
移除成功

2.修改环境变量LD_LIBRARY_PATH

1.演示+验证

环境变量LD_LIBRARY_PATH是OS运行程序时,动态库查找的辅助路径

也就是说OS在运行程序时不仅仅会到系统默认搜索路径当中进行查找,也会到环境变量LD_LIBRARY_PATH当中包含的路径当中进行查找

在这里插入图片描述
在这里插入图片描述

2.修改配置文件+验证

关于环境变量的知识,大家可以看我的这篇博客:
Linux环境变量与命令行参数
我们之前学习过环境变量,知道我们对环境变量的修改只会修改环境变量在内存当中的值
而并不会修改它在磁盘当中的值
每次登陆时环境变量都会重新从磁盘当中进行初始化的
因此如果我们退出Xshell,再重新登录的话,这个环境变量又变成我们修改之前的值了

那么怎么才能彻底修改这个环境变量呢?
只需要修改我们家目录下面的
.bashrc
.bash_profile
即可
在这里插入图片描述
在这里插入图片描述
一般我们不建议修改这些配置文件,因为如果误操作的话会出现很严重的问题

这里我们演示一下
在这里插入图片描述
然后我们退出Xshell,重新登录
然后cd到当前的user目录下,使用这个动态库编译main.c
在这里插入图片描述
成功运行
我们不建议使用第二种方法,因为会修改配置文件
而且我们后面介绍其他知识点,演示的时候会删掉这个动态库的
别出现什么意想不到的问题就得不偿失了

因此我们下面把配置文件修改回之前的样子
在这里插入图片描述
恢复完毕
在这里插入图片描述
请问此时为什么我删掉mytest,重新编译之后
这个mytest还能正常运行呢?

因为我们登上Xshell之后我们的环境变量就已经从磁盘当中读取到内存了,刚才我们修改的是磁盘当中的配置文件,并没有修改内存当中的环境变量
在这里插入图片描述
因此我们退出Xshell并重新登录
然后下面我们介绍第三种方法

3.在/lib64/目录下面建立动态库的软链接文件

因为我们要sudo修改/lib64目录,所以小心为上

1.演示

只要我们在/lib64/目录下面建立libmyc.so这个动态库的软连接文件
这样OS和gcc/g++在搜索库的时候不就能够搜索到我这个库了吗

这点很好理解,下面我们来搞一下

ln -s 目标文件名 链接文件名

在这里插入图片描述
在这里插入图片描述
改成绝对路径之后就成功了

2.验证

下面我们验证一下
在这里插入图片描述
gcc无需-L指明库的路径
OS运行可执行程序成功

因为软链接文件也是一个磁盘级文件,因此退出重登Xshell之后依然存在,依然可以直接编译main.c,运行可执行程序

为了不影响下面的操作,我们删除这个软链接文件
在这里插入图片描述

4.在/etc/ld.so.conf.d目录下添加.conf配置文件

1.演示

在我们系统当中存在一个存放各种配置文件的目录/etc
在这里插入图片描述
在这个目录下面有一个配置文件叫做
ld.so.conf.d
(ld:链接 so:动态库 conf:配置文件 d:表示这个文件是一个目录文件)
这里面允许我们建立各种各样的用户级别配置文件
这里面的配置文件的名字随便起,只要后缀是.conf即可

下面我们在/etc/ld.so.conf.d目录下创建一个my_dynamic_lib.conf配置文件
依旧要sudo创建
在这里插入图片描述
然后我们只需要将libmyc.so的绝对路径sudo写入到这个配置文件当中即可
因为使用sudo写入,而root的vim是没有配置过的,因此是这么个样子
在这里插入图片描述
注意:这里的路径跟我们刚才修改环境变量的时候加的路径一样
都是只到lib,不包含libmyc.so

然后我们还需要使用

sudo ldconfig

这个命令让刚才的配置文件生效
在这里插入图片描述

2.验证

在这里插入图片描述
注意:gcc依旧是要加上-L选项指明库路径的
验证成功

小小建议

我们建议:
如果我们想要拷贝的库很官方,那么建议使用第一种方法,直接安装到系统当中
如果不是很官方,那么建议使用第三种方法,建议软链接文件

因为我们目前的这个库并不是很官方,因此我们采用第三种方法
下面我们删掉这个配置文件,然后建立软链接文件

注意:删除那个配置文件之后,还是要使用sudo ldconfig让删除操作生效
在这里插入图片描述
在这里插入图片描述
建立成功

四.动静态库与动静态链接

1.先上结论

1.动态库只能进行动态链接
2.静态库只能进行静态链接

3.gcc默认使用动态链接,如果我们想要进行动态链接gcc需要加-static选项
4.如果我们同时提供动态库和静态库,gcc默认使用的是动态库
5.如果我们只提供静态库,不提供动态库
那么gcc只能对该库进行静态链接,对能够进行动态链接的库依旧进行动态链接
6.如果我们只提供动态库还非要进行静态链接,那么就会发生链接报错

7.我们的云服务器默认是没有安装c/c++的静态库的,默认只安装了动态库
centos 7中使用yum安装
安装c/c++的静态库的命令是:

C语言静态库:glibc-static 
C++静态库:libstdc++-static
执行
sudo yum install -y glibc-static libstdc++-static

2.验证

其中,第1,2,7条我们无需验证
我们只验证3,4,5,6这4条

1.修改makefile

我们修改一下makefile
让它也能够编译生成静态库
并且也把静态库放到mylib/lib目录当中
在这里插入图片描述
因为makefile的扫描特性
在这里插入图片描述
此时我们需要:
1.make生成静态库
2.make libmyc.so生成动态库
3.make output打包发布动静态库
4.make clean清理动静态库
在这里插入图片描述
成功运行

2.同时提供动态库和静态库,gcc默认使用的是动态库

先拷贝压缩包
在这里插入图片描述
在这里插入图片描述
第一条验证完毕
至于为什么呢?
我们之前提到过动静态库的优缺点,这些就是原因
在这里插入图片描述

3.只提供静态库,不提供动态库

那么gcc只能对该库进行静态链接,对能够进行动态链接的库依旧进行动态链接
在这里插入图片描述

一个小问题

此时我们发现了一个问题:
利用动态库进行动态链接的时候,OS运行该可执行程序时必须要能够找到该动态库,否则就会报错

而我们刚才利用静态库进行静态链接,OS运行该可执行程序时不应该也是找不到该静态库吗?为什么不报错呢?

OS的确找不到该静态库,但是OS不需要找静态库,你给我我也不要
为什么呢?
因为使用对该库使用静态链接编译时,会将对应库函数的具体实现拷贝到可执行程序当中,因此可执行程序当中就有了你这些方法的拷贝

那么我就不需要你这个静态库了,因此我不需要去找你

4.同时提供动态库和静态库,采用静态链接进行编译

在这里插入图片描述
在这里插入图片描述
ldd发现该可执行程序对于所有库都是采用的静态链接的方式进行链接的

5.只提供动态库还非要进行静态链接,那么就会发生链接报错

在这里插入图片描述
验证成功

五.理解动态库的加载

1.站在系统的角度来进行理解

在这里插入图片描述
因此,我们可以得出如下结论:
1.库函数的调用依旧是在进程地址空间当中进行的
2.动态库加载之后,会被映射到进程的共享区当中
3.动态库的代码和数据是OS系统的所有进程中公共的代码和数据,在物理内存当中只需要存在一份即可!
4.如果又有一个进程需要使用已经被加载了的动态库的话,只需要在页表当中建立起共享区和物理内存之间的映射即可

5.OS知道并且决定所有库的加载与否
6.因为系统当中可能会同时存在非常多的已经加载了的库,
因此OS会对这些库进行管理
如何管理?
先描述,在组织

类似于这样的结构体
struct loadlib
{char* libname;//库名称void* addr;//库加载的地址uint64_t time;//库加载的时间结构体的管理字段:例如如果是链表的话,这里就是struct loadlib* next;
};

7.动态库是如何做到共享的呢?

系统当中可能存在很多程序,这些程序可能会用到一些公共方法
我们把这些公共方法打包成库
因为动态库只需要加载一次,就能够映射到每个进程的进程地址空间当中
因此这样就能让所有的进程共享这同一份代码
因此动态库也被称为共享库

而静态库是把对应库函数直接拷贝到程序当中,因此就会造成资源的浪费

而哪些函数需要被打包成库,就看这个函数能否被很多程序所使用

2.补充一下关于可执行程序的知识点

1.先说理论

1.可执行程序本身也是有自己的格式信息的
2.可执行程序本身在加载之前,就已经被按照类别(比如权限,访问属性等)划分为各个区域了
3.可执行程序当中的代码指令本身就是有地址的,这些地址在程序被加载为进程时就会被转化为进程地址空间中的虚拟地址
也就是说进程地址空间当中的很多地址数据都是从可执行程序当中来的

1.验证可执行程序当中代码指令本身就有地址

反汇编是对可执行程序进行操作的,将可执行程序当中的二进制的01序列反汇编翻译为汇编语言

这是用的我自己用哈希桶封装的unordered_map写的测试代码
我们可以看到函数和指令都有自己的地址
在这里插入图片描述
在这里插入图片描述
我们从反汇编当中看到了函数及其指令的地址
因此,我们可以得出可执行程序当中的代码指令本身就是有地址的
因为这些地址是被存放在可执行程序当中的,而可执行程序是被存放在磁盘当中的,因此这些地址被称为逻辑地址

那么这些地址是干什么用的呢?
可执行程序还有什么其他神奇之处吗?

不着急,我们一点一点分析

2.Linux下逻辑地址等于虚拟地址

在学习了进程地址空间之后,我们知道,我们平常在C/C++编程时用到的地址都是虚拟地址
并不是真正的物理地址,进程通过虚拟地址在页表上面的映射找到对应的物理地址
从而实现对数据的访问修改

通过C/C++的学习,我们也知道反汇编当中的地址跟我们代码当中函数/变量的地址是一样的
因此我们就可以得出

Linux下逻辑地址等于虚拟地址

我们又知道:

进程刚被创建时,是先创建进程描述结构体PCB,后将可执行程序的代码和数据加载到物理内存当中

此时我们就知道了,原来当我们的可执行程序将要被加载到物理内存当中之前,
OS创建进程PCB之后,在将可执行程序的代码和数据加载到物理内存当中的同时,在页表当中建立好虚拟地址和物理地址之间的映射

这里的虚拟地址就是可执行程序当中代码和数据的逻辑地址

但是我们又有一个疑问了,
页表当中的虚拟地址我知道是哪来的了,但是进程地址空间的区域是如何划分的呢?
其实也是从可执行程序当中读取的
下面我们就来说明一下

3.可执行程序的表头及其区域划分

可执行程序本身是有自己的格式信息的
它有一个ELF格式的表头
表头当中存放了
1.程序的入口地址(也就是main函数的地址)
2.整个可执行程序的区域划分,以及每个区域的起始地址

因此,可执行程序在执行时,OS先创建进程PCB,
然后根据可执行程序表头当中整个可执行程序的区域划分,以及每个区域的起始地址创建进程地址空间
然后将可执行程序的代码和数据加载到内存当中并在页表建立虚拟地址和物理地址的映射

那么程序的入口地址是干什么用的呢?
CPU是负责执行指令的,因此肯定跟CPU有关

4.CPU当中的程序计数器(pc指针/eip寄存器)

在这里插入图片描述
程序的入口地址是给pc指针的,CPU每次都执行pc指针指向的代码,读取pc指针指向的数据,pc指针从main函数开始一直往下移动,直到执行完整个程序,因此CPU就能跟着pc指针完整地执行完一个可执行程序了

5.理顺可执行程序的执行过程

刚才可能有些分散,现在我们把它串起来,带大家理清一下可执行程序是如何被进程PCB所描述的,如何被CPU所执行的

里面可能有些地方不是特别严谨,但是我们只要能弄清楚这个过程即可

我们简化一下程序的代码
假设我们只有2个函数:main,MyAdd

1.编译器进行编译生成可执行程序

首先,编译器对我们的代码进行编译,生成可执行程序
编译器编译时给每个指令,数据都分配一个逻辑地址,并且将程序的入口地址和整个可执行程序的区域划分存放在表头位置
在这里插入图片描述
编译器生成完可执行程序之后,这个可执行程序带着表头,指令,数据,还有每个指令和数据的逻辑地址,作为一个磁盘级文件,存放在磁盘当中
当我们想要运行该可执行程序时,就要创建进程来运行该程序
进程的创建是OS的任务

2.OS根据可执行程序的信息创建进程PCB

在这里插入图片描述

3.CPU配合pc指针执行可执行程序的指令

在这里插入图片描述

6.重新理解进程地址空间

至此我们看到了,进程地址空间是由OS创建进程时进行初始化
如何初始化? 利用可执行程序当中的信息进行初始化
那些信息从哪来? 编译器编译可执行程序的时候添加上的

因此我们可以得出
进程地址空间,它不是一个单纯的内核数据结构,
它的本质是
由操作系统+编译器+计算机体系结构(CPU)三者共同配合完成的!!

7.理解静态库的静态链接和加载问题

在这里插入图片描述
在这里插入图片描述

3.理解动态库动态链接和加载问题

谈完了可执行程序有关的知识点之后,我们理解动态库的动态链接和加载的问题就能更加清晰明了了

1.理论

首先我们要先说明的是:
动态库的.o文件是采用相对编址的方式进行编址的(也就是-fPIC(产生位置无关码)这个选项的作用)

gcc -fPIC -c mymath.c

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.结论

因此我们可以得出结论:

对于动态库的数据和方法的访问,
都是可以通过该动态库在进程地址空间当中的起始地址加上偏移量的方式来进行访问

这样做有什么好处呢?
当OS创建进程地址空间,将动态库映射到共享区的时候,
无论该动态库加载到物理内存当中的什么位置,映射在共享区的什么位置,都是可以的
这样就能更充分和灵活利用虚拟地址和进程地址空间了

以上就是Linux 动静态库的制作,使用和加载的全部内容,希望能对大家有所帮助!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/781609.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从0开始打架基于VUE的前端项目

准备与版本 安装nodejs(v20.11.1)安装vue脚手架(@vue/cli 5.0.8) ,参考(https://cli.vuejs.org/zh/)vue版本(2.7.16),vue2的最后一个版本初始化项目 创建一个git项目(可以去gitee/github上创建),注意创建一个空项目创建项目vue create mvp-admin-vue-ui删除自己创建的gi…

OpenHarmony实战:烧录Hi3516DV300开发板

前言 烧录开发板是每个开发者的必修课&#xff0c;每次对系统的修改务必进行烧录测试&#xff0c;确保修改正确和不会引入新问题。 本文基于 Windows10&#xff0c;以 Hi3516DV300 开发板为例&#xff0c;指导如何烧录 OpenHarmony 镜像&#xff0c;镜像也叫固件。 这块开发…

C++ :STL中deque的原理

deque的结构类似于哈希表&#xff0c;使用一个指针数组存储固定大小的数组首地址&#xff0c;当数据分布不均匀时将指针数组内的数据进行偏移&#xff0c;桶不够用的时候会像vector一样扩容然后将之前数组中存储的指针拷贝过来&#xff0c;从原理可以看出deque的性能是非常高的…

ISP-VPN实验

文章目录 ISP-VPN实验一&#xff0c;实验拓扑二、实验要求三、IP规划四、实验配置1、IP配置R1的配置R2的配置R3的配置R4的配置R5的配置 2、配置缺省路由3、认证与被认证配置4、HDLC封装5、构建MGRE和GRE6、整个私有网络基于RIP全网可达7、查看路由配置和PC端配置8、PC端pingR5的…

环境影响与碳排放生命周期评估应用及案例分析

生命周期分析 (Life Cycle Analysis, LCA) 是评价一个产品系统生命周期整个阶段——从原材料的提取和加工&#xff0c;到产品生产、包装、市场营销、使用、再使用和产品维护&#xff0c;直至再循环和最终废物处置——的环境影响的工具。这种方法被认为是一种“从摇篮到坟墓”的…

用JSch实现远程传输文件并打包成jar

本文将简单介绍一下 JSch 这个Java的第三方库的一个简单用法&#xff0c;并以此为实例&#xff0c;讲解 IntelliJ 中打包成 jar 包的2种方式。 实现目标 我们的目标是&#xff0c;做出一个jar包&#xff0c;它能够实现类似于 scp 命令的远程传输文件的功能。用法如下&#xf…

应急响应靶机训练-Linux2题解

前言 接上文&#xff0c;应急响应靶机训练Linux2 靶机地址&#xff1a;应急响应靶机-Linux(2) 题解 登录虚拟机&#xff1a; 修改面板密码 提交攻击者IP 答案&#xff1a;192.168.20.1 查看宝塔日志即可 用的net直接是网关 提交攻击者修改的管理员密码(明文) 答案&…

LeetCode---390周赛

题目列表 3090. 每个字符最多出现两次的最长子字符串 3091. 执行操作使数据元素之和大于等于 K 3092. 最高频率的 ID 3093. 最长公共后缀查询 一、每个字符最多出现两次的最长子字符串 非常经典的滑动窗口问题&#xff0c;即动态维护一段区间&#xff0c;使得这段区间满足…

JUC:park/unpark的用法与原理

park / unpark 用法 // 暂停当前线程 LockSupport.park(); // 恢复某个线程的运行 LockSupport.unpark(暂停线程对象)**先说结论&#xff1a;**无论unpark在park前还是后&#xff0c;都可以解除暂停状态。 先park在unpark可以成功运行&#xff1a; Thread t1 new Thread((…

全局UI方法-弹窗二-列表选择弹窗(ActionSheet)

1、描述 定义列表弹窗 2、接口 ActionSheet.show(value:{ title: string | Resource, message: string | Resource, autoCancel?: boolean, confrim?: {value: string | Resource, action: () > void }, cancel?: () > void, alignment?: DialogAlignment, …

kafka学习笔记02(小滴课堂)

Kafka命令行生产者发送消息和消费者消费消息实战 已存在的kafka不能重复创建。 broker设置的是1&#xff0c;factor大于broker了&#xff0c;所以报错。 生产者发送消息&#xff1a; kafka列表出现了新的kafka。 我们使用这个kafka。 我们启动消费者&#xff1a; 我们现在不从…

【Qt 学习笔记】Day1 | Qt 背景介绍

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Day1 | Qt 背景介绍 文章编号&#xff1a;Qt 学习笔记 / 01 文章目录…

C/C++语言实现简易通讯录 [含文件操作,循环双链表]

文章目录 C/C语言实现简易通讯录概要基本功能运行截图展示主要代码展示 &#x1f396; 博主的CSDN主页&#xff1a;Ryan.Alaskan Malamute &#x1f4dc; 博主的代码仓库主页 [ Gitee ]&#xff1a;ryanala [GitHub]&#xff1a; Ryan-Ala C/C语言实现简易通讯录 ⚠⚠⚠ …

话题通信的python实现

一、发布者Publisher的python实现 step1&#xff1a;在scripts文件夹中创建py节点 step2&#xff1a;第一行是为了指定解释器&#xff0c;Ubuntu20.04是python3&#xff0c;比他低的版本是python。第二行是为了指定编码方式。第五行中&#xff0c;引用index.ros.org中数据类型…

RVM安装ruby笔记

环境 硬件&#xff1a;Macbook Pro 系统&#xff1a;macOS 14.1 安装公钥 通过gpg安装公钥失败&#xff0c;报错如下&#xff1a; 换了几个公钥地址&#xff08;hkp://subkeys.pgp.net&#xff0c;hkp://keys.gnupg.net&#xff0c;hkp://pgp.mit.edu&#xff09;&#xff0c;…

论文笔记:Retrieval-Augmented Generation forAI-Generated Content: A Survey

北大202402的RAG综述 1 intro 1.1 AICG 近年来&#xff0c;人们对人工智能生成内容&#xff08;AIGC&#xff09;的兴趣激增。各种内容生成工具已经精心设计&#xff0c;用于生产各种模态下的多样化对象 文本&代码&#xff1a;大型语言模型&#xff08;LLM&#xff09;…

Java:链表

一、链表简介 1、链表与顺序表的区别 上一篇博客我介绍了顺序表&#xff0c;这次我们来认识认识链表&#xff01;先来看看二者的区别&#xff1a; 顺序表&#xff1a;由于顺序表实际上是一个数组&#xff0c;因此它在物理上是连续的&#xff0c;逻辑上也是连续的&#xff01; …

【IDEA】使用debug方式去运行java程序

什么是debug工具&#xff1f; 调试工具&#xff08;debug工具&#xff09;是一种用于帮助程序员识别和修复程序中的错误的工具。它们提供了一系列的功能&#xff0c;帮助程序员在代码执行的过程中跟踪和检测问题&#xff0c;例如查看变量的值、检查函数的调用栈、设置断点来停…

Spring学习——什么是循环依赖及其解决方式

文章目录 前言一、什么是循环依赖二、解决思路1、循环依赖分类2、对象初始化步骤及对象分类3、spring是如何解决的4、图解5、三级缓存1、区别2、ObjectFactory是什么 三、源码debug1、spring创建对象过程1、dubug第一步——找到getBean2、dubug第二步——getBean与doGetBean3、…

腾讯 tendis 替代 redis linux安装使用

下载地址 Tendis存储版 点击下载 linux 解压 tar -zxvf 安装包.tgz cd 解压安装包/scripts 启动 ./start.sh 停止 ./stop.sh 详细配置 修改 /scripts tendisplus.conf # tendisplus configuration for testing # 绑定本机IIP bind 192.168.31.112 port 51002 #设…