动态库学习[通俗易懂]

动态库学习[通俗易懂]总结一:动态库前言 我们知道程序编译链接经常使用动态,同时我们可能还知道,动态库时程序运行时加载的。但是动态库到底有什么作用,如何生成、如何加载等,我们却很少关注。接下来,我给大家做一个简单的介绍。1.1动态库和静态库的区别静态库特点(linux):命名上是以*.o结尾静态库在链接阶段直接就加入到可执行的文件中了,在执行过程中无需该静态库相对于动态库生成的文件,使用静态库生…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用


总结一:动态库

前言


我们知道程序编译链接经常使用动态,同时我们可能还知道,动态库时程序运行时加载的。但是动态库到底有什么作用,如何生成、如何加载等,我们却很少关注。接下来,我给大家做一个简单的介绍。

1.1 动态库和静态库的区别

静态库特点(linux):

  • 命名上是以 *.o 结尾
  • 静态库在链接阶段直接就加入到可执行的文件中了,在执行过程中无需该静态库
  • 相对于动态库生成的文件,使用静态库生成的文件连接生成的可执行文件较大

动态库的特点(linux)

  • 命名上是以 *.so
  • 目标文件在链接阶段只是指明链接的那个动态库,动态库与目标文件保持独立。在执行过程中需要该动态库
  • 使用动态库生成的目标文件较小

对于工程中比较共通的源码文件,比如多个进程使用同一个模块的源码,我们最好将其制作成动态库,以节省系统空间。同时如果动态库出现bug,只需要重新生成一个动态库并将以前的替换即可。不需要重新编译其他模块。

1.2 内存中的动态库

在讲到动态库的装载时我们需要懂一定的背景知识,首先虚拟内存和物理内存,其次还有地址映射,这些知识就不在本文多加讲解,网上资料很多。我们动态库在整个内存空间是有一份,而每个进程都有自己的虚拟空间,虚拟空间会使用匿名映射(mmap使用MAP_PRIVATE方式进行映射),使自己的进程与动态库进行关联。本进程只会保留访问动态库时的一些数据。好了,打的方向就说这么多,这几句话随便抽出一个词来都够将好久。我们只需要对大方向有认识即可。以下就是映射表

在这里插入图片描述

此外我们说一些额外的小知识,在linux系统中 /proc 目录下有很多进程文件。在执行的进程都会创建一个文件。随便进入一个文件 /etc/1892。查看maps文件(sudo cat maps),这里面对应了动态库对应虚拟空间的位置,值得注意的是,这个文件最后一列有多少个[stack]就有多少个线程

00008000-005ba000 r-xp 00000000 b3:01 11822      /usr/local/bin/ecTelematicsApp
005c1000-005df000 rw-p 005b1000 b3:01 11822      /usr/local/bin/ecTelematicsApp
005df000-00d7f000 rw-p 00000000 00:00 0          [heap]
a8c00000-a8cff000 rw-p 00000000 00:00 0 
a8cff000-a8d00000 ---p 00000000 00:00 0 
a8e00000-a8e01000 ---p 00000000 00:00 0 
a8e01000-a9600000 rw-p 00000000 00:00 0          [stack:1369]
a9600000-a9700000 rw-p 00000000 00:00 0 
a9700000-a9721000 rw-p 00000000 00:00 0 
a9721000-a9800000 ---p 00000000 00:00 0 
.......
b5c5d000-b645e000 rw-p 00000000 00:00 0          [stack:1961]
b645e000-b6470000 r-xp 00000000 b3:01 709        /lib/libresolv-2.19.so
b6470000-b6477000 ---p 00012000 b3:01 709        /lib/libresolv-2.19.so
b6477000-b6478000 r--p 00011000 b3:01 709        /lib/libresolv-2.19.so
b6478000-b6479000 rw-p 00012000 b3:01 709        /lib/libresolv-2.19.so
b6479000-b647b000 rw-p 00000000 00:00 0 
.......
bea5d000-bea7e000 rw-p 00000000 00:00 0          [stack]
ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]

同时我们也可以直接使用readelf -m [bin文件名],来读取该bin文件的链接信息。

1.3 动态库的加载

关于动态库我当初的直接认识是,程序运行到调用该动态库的接口时,会产生缺页,从而去磁盘加载动态库到内存,然后再执行。但事实并非如此。动态库也分为隐式链接和显示链接,不同的方式其载入内存的时间也是大相径庭。

显示链接 隐式链接
语法 不需要申明动态库先关的头文件,在调用时需要加载动态库的名称 只需要添加相应的头文件即可
加载 执行到相应代码段时加载动态库(可以控制库的加载和卸载) 由系统控制加载时间,一般在程序启动时就加载

由以上两点我们可以看出显示链接如果控制得当,对内存的消耗将下降许多,大型项目应该使用显示链接。但是缺点也有,就是如果库不存在,隐式链接可以再一开始就发现库不存在,而显示链接会被偶然触发。

1.4 动态库的制作

首先我们准备一个源文件 print.c

#include<stdio.h>

void printInter()
{
    printf("%s\n", __FUNCTION__);
}

void printExtern()
{
    printInter();
    printf("%s\n", __FUNCTION__);
}

输入指令

gcc -fPIC -c print.c -o print.o
gcc -shared print.o -o libprint.so -lstdc++

由此我们生成了 libprint.so动态库。

然后我们再创建libcurl.so 的接口头文件print.h

#ifndef __PRINTT_H_
#define __PRINT_H__

void printExtern();

#endif

1.5 动态库的隐式链接

我们创建main.c 去使用库

#include<stdio.h>
#include<unistd.h>
#include "print.h"

int main()
{
	printf("waite 5 seconds\n");
	sleep(5);
	printf("%s\n", __FUNCTION__);
	printExtern();

	return 0;
}

输入指令

gcc -fPIC -c main.c -o main.o
gcc -o target main.c -L./ -lprint -I./ -lstdc++

生成target

执行 ./target, 输出如下

waite 5 seconds
main
printInter
printExtern

我们可以通过 readelf -d target,可以查看target链接了libprint.so

root@user:/home/cjj/jianxiongs/so2# readelf -d target 

Dynamic section at offset 0xf0c contains 25 entries:
  标记        类型                         名称/值
 0x00000001 (NEEDED)                     共享库:[libprint.so]
 0x00000001 (NEEDED)                     共享库:[libc.so.6]
 0x0000000c (INIT)                       0x80483fc
 0x0000000d (FINI)                       0x8048604
 0x00000019 (INIT_ARRAY)                 0x8049f00
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x0000001a (FINI_ARRAY)                 0x8049f04
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x6ffffef5 (GNU_HASH)                   0x80481ac
 0x00000005 (STRTAB)                     0x80482c8
 0x00000006 (SYMTAB)                     0x80481e8
 0x0000000a (STRSZ)                      208 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x804a000
 0x00000002 (PLTRELSZ)                   32 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x80483dc
 0x00000011 (REL)                        0x80483d4
 0x00000012 (RELSZ)                      8 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x80483b4
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x8048398
 0x00000000 (NULL)                       0x0

接下来我们删除 libprint.so。 然后再运行target,这时我们发现出现如下错误

./target: error while loading shared libraries: libprint.so: cannot open shared object file: No such file or directory

这说明程序在一开始就要加载libprint.so的库,这就是动态库的隐式链接

1.6 动态库的显式链接

这时我们需要更改main.c的内容,修改最终如下

#include<stdio.h>
#include<unistd.h>
#include<dlfcn.h>
#include<stdlib.h>
//#include "print.h"  删除引用头文件

void test()
{
    char *libname = NULL;
    char *err = NULL;

    //open the lib
    void *handle = dlopen("./libprint.so", RTLD_NOW);
    if(!handle)
    {
        err = dlerror();
        printf("Load libprint.so failed : %s \n", err);
        exit(1);
    }
    //clear error info
    dlerror();

    typedef void (*pf_t)(void);
    pf_t print = (pf_t)dlsym(handle, "printExtern");

    err = dlerror();

    if(err)
    {
        printf("fail to find function : %s \n", err);
        exit(1);
    }

    print(); //使用函数指针的方式引用,不能直接引用

    //close the lib
    dlclose(handle);
    if(dlerror())
    {
        printf("close libprint.so failed : %s \n", dlerror());
        exit(1);
    }

    printf("%s\n", __FUNCTION__);

}


int main()
{
    printf("waite 5 seconds\n");
    sleep(5);

    test();
    return 0;
}

同时我们gcc 中还需要加上 libdl.so库

gcc -fPIC -c main.c -o main.o
gcc -o target main.c -L./ -lprint -I./ -lstdc++ -ldl

此时我们删除 libprint.so。然后再执行target 文件。发现程序运行了一段时间,到使用库时才报错

Load libprint.so failed : ./libprint.so: cannot open shared object file: No such file or directory 

由此我们可以看出动态库的显示连接才能真正实现使用时才去调用。

附录 : makefile

在这里我给出这个小工程的makefile

CXX = g++
CC = gcc
FLAGS = -fPIC
TARGET = target
CSOURCE = $(wildcard ./*.c)
COBJS = $(CSOURCE:.c=.o)

target: $(COBJS) libprint 
	$(CC) $(FLAGS) -o $(TARGET) main.o -L./ -lprint -I./ -ldl

%.c:%.o
	$(CC) $(FLAGS) -c $< -o $@

libprint:
	$(CC) $(FLAGS) -shared -o libprint.so print.o

rm:
	rm print.o

clean:
	-rm -rf $(COBJS) $(TARGET) libprint.so

test:
	@echo $(CSOURCE)
	@echo $(COBJS)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/187491.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • WebService简单案例实例

    WebService简单案例实例本周工作日即将结束,下周项目经理安排了一项任务可能需要使用到webservice,但本人之前尚未使用过,网上查了一些案例看了看在此小记一篇留作日后回首也希望可以帮助到查看者朋友1、什么是WebService?WebService是一种远程调用技术,也叫XMLWebServiceWebService,是一种可以接收从Internet或者Internet上的其他系统中传递过来的请求,轻量级的独…

    2022年7月21日
    13
  • idea2021.11激活码【2021免费激活】[通俗易懂]

    (idea2021.11激活码)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.htmlCEX82FN4RV-eyJsa…

    2022年3月29日
    59
  • RouterOS(ROS)设置动态域名(DDNS)「建议收藏」

    RouterOS(ROS)设置动态域名(DDNS)「建议收藏」使用DDNS把动态IP地址映射到一个固定的域名解析服务上,用户每次连接网络的时候客户端程序通过信息传递把该主机的动态IP地址传送给服务器程序,服务项目程序提供DNS服务并实现动态域名解析。添加一个Scheduler,system-&gt;Scheduler::globalddnsiptemp[/ipaddresget[/ipaddressfindinterface=…

    2022年5月9日
    243
  • python手动抛出异常能正常启动_python数组去掉第一个元素

    python手动抛出异常能正常启动_python数组去掉第一个元素try:print(‘正常执行’)#根据业务逻辑判断,需要手动抛出异常raiseException(print(a))#raiseException(‘print(a)’)#注意这两个的区别,这个带字符串,直接打印字符串里的内容,python把字符串的内容一字不差解析成了异常并打印出来print(‘正常结束’)exceptExcepti…

    2022年10月18日
    0
  • IST:Iterative Shrinkage/Thresholding和Iterative Soft Thresholding

    IST:Iterative Shrinkage/Thresholding和Iterative Soft Thresholding本篇是对压缩感知重构算法之迭代软阈值(IST)的延续,可能需要以下基础:软阈值(SoftThresholding)函数和硬阈值(HardThresholding)函数。前面我们在讨论迭代软阈值算法时提到,一般文献中出现的IST或ISTA简称中的“S”并非指的是“soft”,而是“shrinkage”,即“IterativeShrinkage/ThresholdingAlgorithm”,那么IterativeSoftThresholding和IterativeShrinkage/

    2022年6月10日
    25
  • 直方图均衡化和图像平滑 实验报告

    直方图均衡化和图像平滑 实验报告

    2021年7月5日
    81

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号