【Linux】零基础深入学习动静态库+深入学习地址

文章目录

前提:

把自己的方法给别人用,有两种方法

  • 把自己的源代码给他
  • 把自己的方法打包成库(库+.h)

静态库

原理

把.c文件汇编成.o文件后,把.o文件打包成.a文件
再将.a文件和main.c链接起来
静态库链接时会把库代码连接到可执行文件中,从此可执行文件不需要静态库

静态库命名标准是 lib+库名+.a

实现

mymath.h

c 复制代码
#pragma once
#include <stdio.h>
extern int myerrno;

int add(int x,int y);
int sub(int x,int y);
int mul(int x,int y);
int div(int x,int y);

mymath.cpp

c 复制代码
#pragma once
#include "mymath.h"
int myerrno=0;
int add(int x,int y)
{
  return x+y;
}
int sub(int x,int y)
{
  return x-y;
}
int mul(int x,int y)
{
  return x*y;
}
int div(int x,int y)
{
  if(y==0)
  {
    myerrno=1;
    return -1;
  }
  else{
  return x/y;
  } 
}

Makefile

c 复制代码
static-lib=libmymath.a
$(static-lib):mymath.o
	ar -rc $@ $^
mymath.o:mymath.cpp
	g++ -c $^


.PHONY:clean
clean:
	rm -rf *.o *.a

.PHONY:output
output:
	mkdir -p lib/include
	mkdir -p lib/mylib
	cp *h lib/include 
	cp *a lib/mylib

ar是gnu归档工具,rc(replace and create)

main.cpp

c 复制代码
#include "mymath.h"
int main()
{
  extern int myerrno;
  int x=div(10,0);
  
  printf("%d\n",sub(1,6));
  printf("10/0=%d errno=%d\n",x,myerrno);
  
  return 0;
}

链接:

g++ main.cpp -L. -lmymath

-L后面加库路径 -l加库名称
记得去掉lib和.a编译器会自动补齐

🚩动态库

把.c文件汇编成.o,再把.o打包成.so
将main.c链接动态库所在的路径

相比静态库直接加载到可执行程序中,动态库程序运行时才去链接动态库,多个程序共用动态库,节省内存和硬盘空间

实现

myprint.h

c 复制代码
#pragma once
#include <stdio.h>
void print();

myprint.cpp

c 复制代码
#include "myprint.h"

void print()
{
  printf("hello new world!\n");
  printf("hello new world!\n");
  printf("hello new world!\n");
}

mylog.h

c 复制代码
#pragma once
#include <stdio.h>

void log(const char*);

cat mylog.cpp

c 复制代码
#include "mylog.h"
void log(const char* info)
{
  printf("warning!:%s\n",info);
}

Makefile

c 复制代码
dy-lib=libmymethod.so

$(dy-lib):mylog.o myprint.o
	g++ -shared -o $@ $^
mylog.o:mylog.cpp
	g++ -fPIC -c $^
myprint.o:myprint.cpp
	g++ -fPIC -c $^

.PHONY:clean
clean:
	rm -rf *.o *.a lib *.so

.PHONY:output
output:
	mkdir -p lib/include
	mkdir -p lib/mylib
	cp *h lib/include 
	cp *a lib/mylib
	cp *so lib/mylib

mytest.cpp

c 复制代码
#include "myprint.h"
#include "mylog.h"

int main()
{
  log("log function");
  print();
  return 0;
}

g++ mytest.cpp -o mytest -L. -lmythod
make output

然后创建test,我们把lib移进test,

我们在test目录里把main.cpp链接动态库会发生什么?

g++ main.cpp -o test -I./lib/include -L./lib/mylib -lmymath -lmymethod

-I 是寻找找头文件
再执行test文件

出问题了!!
之前是动态库文件和可执行文件在同一目录

现在分开链接,加载器(系统)不知道动态库在哪里

我不是g++ -L告诉你了吗??

那是告诉编译器的

现在是可执行文件,与编译器无关了

🚩解决问题

怎么解决!!!
最常用的也是最简单的方法,为什么我们执行C语言程序,没出问题,因为系统会自动去默认特定目录去寻找动态库

我们只需要把我们自己的库复制到系统目录即可

sudo cp lib/mylib/libmymethod.so /usr/lib/


我们一般都是把库装到系统中

或者动态库软链接到系统目录中

sudo ln -s /home/jib/1010/static-dy-library/libmymethod.so /usr/lib/libmymethod.so

然后刷新下动态库

sudo ldconfig

🚩动态库加载原理

动态库使用是要被加载到内存中的,常见的动态库要被所有可执行程序(动态链接)使用,

所以动态库是要被所有进程共享的

怎么做到的?

先从.exe入手,启动进程,正文代码地址连接到页表中,文件系统将文件从硬盘中加载到物理内存中,分为数据和代码,连接到页表中

其实动态库也相当于.exe文件,也需要加载到物理内存中,但是进程那边,关于动态库的代码会被放入共享库中,正文代码需要动态库就去共享库寻找,再通过页表与物理内存中.so连接==,完成之后返回到正文代码中,

此时,再开一个进程,该进程与其他进程毫不相干,但是依旧采用上述过程,实现了动态库的共享

事实上,系统在运行中,会存在很多动态库,OS会把他们管理起来,先描述,再组织,

系统中,所有动态库的加载情况,操作系统一清二楚

结论:页表建立映射关系之后,我们执行的代码都是在虚拟内存地址中解决

🚩-fPIc

意思为与地址无关码,编译动态库时使用,也是动态库实现的关键

先说一个问题,两个进程的虚拟地址表相同的虚拟地址映射的物理地址是相同的吗

:不一定,对于共享区和只读数据段是相同的,因为他们不会被修改

对于可读写数据就不相同,要确保进程的隔离性

-fPIc 从全局变量中偏移一段地址,简单来说就是,从某个大家都知道的位置(GOT全局偏移表),移动一段地址就能找到动态库

没有-fPIc怎么样?
如果没有-fPIc,我们存到一个进程的虚拟地址表中,等到又来一个进程,因为相同虚拟地址映射物理地址不同,我们的动态库地址可能已经被占用了,
即使不被占用,我们还要再存储一遍,物理内存中又要再复制一遍动态库代码,这就违反了共享库原则

就好比大家都知道40米处有棵树,往后偏移10米存储动态库

🚩关于地址

程序未加载之前的地址

编译器会考虑操作系统,

事实上,在代码编译成功之后,硬盘中就存在虚拟地址(其实叫做逻辑地址)了,

地址以平坦模式存储在硬盘中(之前把地址分段隔开,要寻找地址非常麻烦,平坦模式中段依然存在,但是段和段之间连续,偏移量看作线性地址,当作0-4GB的连续平坦的空间)

程序被加载之后

整个流程分析!!

1,编译器编译代码,形成逻辑地址存在硬盘中

2,进程创建出来,并且分配虚拟地址空间,同时逻辑地址直接当作虚拟地址使用

3,硬盘中的数据加载到物理内存中,同时建立页表,把虚拟地址和物理地址建立映射

4,CPU从EIP/PC中取得虚拟地址,再通过页表翻译成物理地址,然后访问物理地址指令/数据,执行代码

此外,进程启动时,会把入口虚拟地址直接给EIP/PC,CPU执行完该地址之后,EIP会跳转到下一条物理地址

相关推荐
不甘平凡的小鸟2 小时前
libcurl+vs2017+openssl编译
linux·运维·服务器
_OP_CHEN2 小时前
【从零开始的Qt开发指南】(十四)Qt 窗口之“三剑客”:工具栏、状态栏、浮动窗口进阶实战指南
开发语言·c++·qt·前端开发·gui开发·qt窗口
jiecy2 小时前
IPv6 过渡 - 隧道技术
运维·网络·信息与通信
Noushiki2 小时前
RabbitMQ 基础 学习笔记1
笔记·学习·rabbitmq
知识分享小能手2 小时前
Ubuntu入门学习教程,从入门到精通, Ubuntu 22.04中的任务计划详解(16)
linux·学习·ubuntu
oMcLin2 小时前
CentOS 7.9 使用 SELinux 时无法访问特定目录:如何配置 SELinux 策略允许访问
linux·运维·centos
geniuscrh2 小时前
自建Tailscale的Derp服务器
运维·服务器
我命由我123452 小时前
Photoshop - Photoshop 工具栏(53)画板工具
笔记·学习·职场和发展·求职招聘·职场发展·学习方法·photoshop
QT 小鲜肉2 小时前
【Linux命令大全】001.文件管理之whereis命令(实操篇)
linux·运维·服务器·网络·笔记