动态链接

2023-11-17

动态链接

命令:

gcc:

  • -static:产生静态库
  • -shared:产生共享库

readelf:

  • -d:查看.dynamic段的内容

ldd:

查看一个程序主模块或一个共享库依赖于哪些共享库

一.静态链接和动态链接的优缺点:

静态链接:

  • 空间的浪费:静态链接,程序最后都会链接成一个可执行文件,那么功能相同的模块(可以用来共享),在每一个需要使用的程序中都有一个份,这样就会对计算机的内存和磁盘空间造成浪费。
  • 更新、部署、发布困难:当需要更新功能相同的模块(可以用来共享)时,所有的程序需要重新连接。

动态链接:

不对那些组成程序的目标文件进行链接,等到程序要运行时才进行链接。

  • 节省空间,提供缓存命中率:在内存中共享一个目标文件模块的好处不仅仅是节省内存,还可以减少物理页面的换入换出,也增加CPU缓存的命中率。
  • 加强兼容性:动态链接库相当于在程序和操作系统之间增加了一个中间层,从而消除了程序对不同平台之间依赖的差异性。
  • 时间换取空间:动态链接是把链接这个过程从本来的程序装载前被推迟到装载的时候,这样的做法很灵活,但是程序每次被装载时都要进行重新链接,会有性能的损失。
  • DLL Hell:当一个程序所依赖的某个模块更新后,由于新的模块与旧的模块之间接口不兼容,导致其他使用了这个模块的程序无法正常运行。在早起的Windows版本中经常会遇到这样的问题,这个问题也经常被称为Dll Hell
二.动态链接:
2.1 动态链接过程中的外部符号引用:

链接器在链接生成可执行文件时,如果一个使用了的函数定义在动态共享库中,由于动态库共享库保存了完整的符号信息(因为运行时进行动态链接还需要使用符号信息),链接器可以得知该符号是定义在动态库,就会标记其为一个动态链接符号,不对它进行重定位,把重定位过程留到装载时进行。

2.2 地址空间分布:

对于静态链接的可执行文件来说,整个进程只有一个文件要被映射,那就是可执行文件本身。但是对于动态链接来说,除了可执行文件本身之外,还有它依赖的共享目标文件。

通过readelf -l xx.so可以发现共享对象的装载地址是0x00000000,是一个无效地址,因此共享对象的最终转载地址在编译时是不确定的。

2.3 地址无关代码:

2.3.1 固定装载地址的困扰

程序模块的指令和数据中可能会包含一些绝对地址的引用,在链接产生输出文件的时候,就要假设模块被装载的目标地址。

如果共享对象的装载地址是固定,那么共享对象的装载地址不能由每个程序来决定了,而是由系统来管理。比如程序1使用了共享对象A和B,如果程序1将A和B的地址分配在0x1000-0x2000,0x2000-0x3000,当程序2编译的时候,用到了共享对象A和C,在地址分配的时候,由于不知道0x2000-0x3000已被分配给了共享对象B,然后把共享对象C分配在0x2000-0x3000。那么任何一个程序将不能同时使用共享对象B和C,因为它们的装载地址是一样的。

早起有些系统就采用了这种的做法,这种做法叫做“静态共享库”,静态共享库的做法是将程序的各种模块统一交给操作系统来管理,操作系统在某个特定的地址划分出一些地址块,为那些已知的模块预留足够的空间。

静态共享库的问题:

  1. 上面提到的地址冲突问题;
  2. 静态共享库升级,必须保持共享库中的全局函数和变量地址不变,因为程序在链接的时候就已经绑定了这些地址,否则需要重新链接程序;
  3. 由于静态共享库在一开始分配空间的时候是有限空间,所以新增的函数和变量会受到空间的限制。如果超出了之前预留的空间,就会出现与其他静态共享库地址冲突问题。

2.3.2 装载时重定位:

为了解决静态共享库的问题,共享对象在编译时不能假设自己的装载地址,那么程序链接的时候也就不能确定那些绝对地址是什么。这样共享对象需要能够在任意地址装载,基本思路是:在链接时,对所有绝对地址的引用不作重定位,把这一步推迟到装载时再完成,一旦模块装载地址确定,即目标地址确定,那么系统就对程序中所有的绝对地址引用进行重定位。

在链接时的重定位叫做链接时重定位,装载时的重定位叫做装载时重定位,在Windows中也叫基址重置

共享对象也就是动态链接库在被装载到物理内存后,始终是只有一份的,不管有多少个进程使用它。但是对于每一个进程,共享对象会映射一次到虚拟地址空间,也就是每个进程空间都有一份共享对象的映射,此时,对于不同的进程,映射的地址(基址)是不一样的(大部分情况下)。紧接着,进行装载时重定位。装载时重定位由动态链接器完成,动态链接器会被一起映射到进程空间中。它根据共享对象在虚拟内存空间中的地址修改在物理内存中的共享对象中的指令,为什么会修改指令,原因在于绝对地址访问(如模块内的变量访问)是直接用mov指令完成的,也就是直接将地址打入寄存器,所以,此时的重定位会直接修改指令。进一步,共享对象中修改的指令是根据共享对象被映射到虚拟空间中的地址(基址)决定的,而每个进程对共享对象的映射不可能都是在相同地址。所以也就无法完成这一部分代码的共享。

2.3.4 地址无关代码

装载时重定位解决了应用程序对引用的共享对象的全局函数和变量寻址的问题,也解决了共享对象对引用的其他共享对象的全局函数和变量寻址的问题。

装载时重定位需要修改指令,如果共享对象中的部分指令需要在装载时修改时,那么同一份指令就不能被多个进程共享了,因为指令被重定位后对于每个进程来说是不同的,那么就失去了动态链接节省内存(指令共享)的一大优势。这是对于共享对象引用其他共享对象的全局函数和变量的情况。虽然装载时重定位失去了共享的特点,但是运行速度还是要比地址无关代码要快。

为了解决这个问题,基本思路:将指令中需要修改的部分分离出来,跟数据部分放在一起(动态链接库中的可修改数据部分对于不同进程来说有各自的副本),这种技术叫做“地址无关代码”。GCC产生地址无关代码只需要使用“-fPIC”参数即可,注意:“-fPIC”是针对共享对象的,而不是可执行文件的。

共享对象模块中的四种地址引用方式:

  1. 模块内部的函数调用、跳转等:

    调用者与被调用者处于同一模块,所以模块内部的跳转、函数调用、都可以是相对地址调用,或者基于寄存器的相对调用,这种指令不需要重定位。也就是说不管模块是装载到哪个位置,这种指令都是有效的。当然还有个“全局符号介入”的问题,后面会讲到。

  2. 模块内部的数据访问,比如全局变量、静态变量:

    一个模块前面一般是若干个页的代码,后面紧跟着若干个页的数据,这些页之间的相对位置是固定的,也就是说任何一条指令与它需要访问的模块内部数据之间的相对地址是固定的,那么只需要相对于当前指令加上固定的偏移量就可以访问模块内部数据了。

  3. 模块外部的函数调用、跳转等:

    模块间的数据访问目标地址要等到装载时才决定。ELF的做法是在数据段里面建立一个指向这些变量的指针数组,称为全局偏移表(Global Offset Table,GOT),链接器在装载模块的时候会查找每个变量所在的地址,然后填充到GOT中的各个项,由于GOT本身是在数据段,所以它可以在模块装载时被修改,且每个进程都有独立的副本。当指令需要访问这个变量(外部变量)时,由于模块内,指令与GOT的偏移量在编译期就确定了,所以当前指令通过加上一个固定的偏移量可以访问到GOT的位置,然后根据变量在GOT里的偏移量可以得到变量的地址,然后通过这个地址可以访问到变量的值。

  4. 模块外部的数据访问,比如定义在其他模块的全局变量:

    模块间调用和跳转,采用类似于上面呢类型3的方法,只是与之不同的是GOT中相应的项保存时目标函数的地址。

如何区分一个DSO是否为PIC:

readelf -d foo.so | grep TEXTREL

如果上面的指令有任何输出,那么foo.so就不是PIC的,否则就是PIC的。因为PIC的DSO是不会包含代码段重定位表的,TEXTREL表示代码段重定位表地址。

PIC与PIE:

地址无关代码可以用在共享对象可执行文件,产生地址无关可执行文件的方法,GCC添加-fPIE参数。

共享模块的全局变量问题:

extern int global;
int foo()
{
	global = 1;
}

当编译器编译module.c时,它无法根据这个上下文判断global是定义在同一个模块的其他目标文件还是定义在另外一个共享对象之中,即无法判断是否为跨模块间的调用。

假设module.c是程序可执行文件的一部分,程序主模块的代码并不是地址无关代码,也就是说代码不会使用这种类似于PIC机制,它引用这个全局变量的方式跟普通数据访问方式一样,编译器会产生这样的代码:

movl $0x1, XXXXXXXX

XXXXXXXX就是global的地址。由于可执行文件在运行时并不进行代码重定位,所以变量的地址必须在链接过程中确定下来。为了能够使得链接过程正常进行,链接器会在创建可执行文件时,在它的“.bss”段创建一个global变量的副本。那么问题就很明显了,现在global变量定义在原先的共享对象中,而在可执行文件的“.bss”段还有一个副本,如果同一个变量同时存在多个位置中,这在程序实际运行过程中肯定不可行的。

解决的办法只有一个,所有的使用这个变量的指令都指向位于可执行文件中的那个副本。ELF共享库在编译时,默认都把定义在模块内部的全局变量当做定义在其他模块的全局变量,也就是说当作前面的类型四,通过GOT来实现变量的访问。

  1. 全变变量在可执行文件中拥有副本:动态链接器会把GOT中的相应地址指向该副本;
  2. 变量在共享模块初始化:动态链接器需要将初始化值复制到程序主模块中的变量副本中;
  3. 全局变量在程序主模块中没有副本:GOT中的相应地址指向模块内部的该变量副本。
2.4 延迟绑定实现:

一般在动态链接中,程序模块之间函数引用比较多(全局变量如果多的话,会使得模块之间耦合度比较高)。延迟绑定的基本思想:当函数第一次被用到时才进行绑定(符号查找、重定位等)。这么一来大大加快了程序的启动速度。ELF采用**PLT(Procedure Linkage Table)**的方法来实现。

ELF将GOT拆分成了两个表叫做“.got”和".got.plt"。其中“.got”用来保存全局变量引用的地址,“.got.plt”用来保存函数引用的地址。PLT在ELF文件中以独立的段存放,段名通常叫做“.plt”,因为它本身是一些地址无关的代码,所以可以跟代码段等一起合并成同一个可读可执行的“Segment”被装载入内存。

2.5 动态链接相关结构:

动态链接的基本步骤:

装载可执行文件 -> 启动动态链接器本身 -> 装载所有需要的共享对象 -> 动态链接器的重定位操作和初始化操作 -> 将控制权交给可执行文件的入口地址,程序开始执行。

2.5.1 “.interp”段:

interp是interpreter(解释器)的缩写,里面保存的就是一个字符串,这个字符串就是可执行文件所需要的动态链接器的路径。

2.5.2 “.dynamic”段

动态链接ELF中最重要的结构就是“.dynamic”段。这个段里面保存了动态链接器所需要的基本信息:依赖于哪些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象初始化代码的地址等。可以通过readelf -d xx.so来查看“.dynamic”段的内容。Linux还提供了一个命令用来查看一个程序主模块或一个共享库依赖于哪些共享库:

ldd Program1

.dynamic结构:

typedef struct{
	ELF32_Sword d_tag;
	union{
		ELF32_Word d_val;
		ELF32_Addr d_ptr;
	}
}
d_tag类型 d_un的含义
DT_SYMTAB 动态链接符号表的地址,d_ptr表示“.dynsym”的地址
DT_STRTAB 动态连你姐字符串表地址,d_ptr表示“.dynstr”的地址
DT_STRSZ 动态链接字符串表大小,d_val表示大小
DT_HASH 动态链接哈希表地址,d_ptr表示“.hash”的地址
DT_SONAME 本共享对象的“SO-NAME”
DT_RPATH 动态链接共享对象的搜索路径
DT_INIT 初始化代码地址
DT_FINIT 结束代码地址
DT_NEED 依赖的共享对象文件,d_ptr表示所依赖的共享对象文件名
DT_REL、DT_RELA 动态链接重定位表地址
DT_RELENNT、DT_RELAENT 动态重定位表入口数量

2.5.3 动态符号表(.dynsym)

对于Program1程序依赖于Lib.so,引用了里面的foobar()函数,那么称Program1导入(Import)了foobar函数,foobar是Program1的导入函数,其实该符号就叫做“符号引用”。对于Lib.so来说,Lib.so导出(Export)了foobar函数,foobar是Lib.so的导出函数,其实该符号就叫做”符号“。

.dynsym只保存了与动态链接相关的符号,而.symtab则保存了所有符号。

2.5.4 动态链接重定位表

共享对象需要重定位表的主要原因是导入符号的存在。在动态链接中,导入符号的地址在运行时才确定,所以需要在运行时将这些导入符号的引用修正,即需要重定位。

如果一个共享对象不是以PIC模式编译的,那么需要在装载时被重定位。如果一个共享对象是PIC模式编译的,也需要重定位。

动态链接的文件中,也有类似的重定位表分别叫做”.rel.dyn“和”.rel.plt“,”rel.dyn“实际上是对数据引用的修正,它修正的位置位于”.got“以及数据段;而”.rel.plt“是对函数引用的修正,它所修正的位置位于”.got.plt“。如果某个ELF文件是以PIC模式编译的,并调用了外部函数bar,则bar会出现在”.rel.plt“中;而如果不是以PIC模式编译,则bar将出现在”.rel.dyn“中。

2.5.5 全局符号介入

当一个可执行文件需要依赖两个库a.so、b.so,且两个共享对象中有名称相同的全局函数,那么当一个符号需要被加入全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略,这就叫做全局符号介入

2.6 显示运行时链接:

从文件本身的格式上来看,动态库实际上跟一般的共享对象没有区别,主要的区别是:共享对象是由动态链接器在程序启动之前负责装载和链接的,这一系列步骤都由动态链接器自动完成,对于程序本身是透明的。而动态库的装载则是通过一系列由动态链接器提供的API:打开动态库(dlopen)、查找符号(dlsym)、错误处理(dlerror)、关闭动态库(dlclose)。

2.6.1 dlopen()

void * dlopen(const char *filename, int flag);

dlopen的返回值是被加载的模块的句柄,这个句柄可以在其他API中使用。如果模块已经通过dlopen被加载过了,那么返回的是同一个句柄。

参数:

  • filename:加载动态库的路径;如果filename这个参数设置为0,那么dlopen返回的将是全局符号表的句柄,也就是我们可以在运行时找到全局符号表里面的任何一个符号,并且执行它们,这有些类似高级语言反射的特性。

  • flag:表示函数符号的解析方式。

    • RTLD_LAZY:表示使用延迟绑定,当函数第一次被用到时才进行绑定,即PLT机制;
    • RTLD_NOW:表示当模块被加载时即完成所有的函数绑定工作,如果有任何未定义的符号引用的邦迪你那个工作没法完成,那么dlopen()就返回错误;
    • RTLD_GLOBAL:可以跟上面两个常量一起使用,表示将被加载的模块的全局符号合并到进程的全局符号表中,使得以后加载的模块可以使用这些符号。

2.6.2 dlsym

void * dlsym(void *handle, char *symbol);

dlsym()找到了相应的符号,则返回该符号的值;没有找到相应的符号,则返回NULL。

(1)如果查找的符号是个函数,那么它返回的是函数的地址;

(2)如果查找的符号是个变量,那么它返回变量的地址;

(3)如果这个符号是个常量,那么它返回的是该常量的值。

参数:

  • handle:dlopen()返回的动态库句柄;
  • symbol:需要查找的符号的名字,一个以“\0”结尾的C字符串;

注意:如果常量的值刚好是NULL或者0,那么需要通过dlerror()函数可以判断是常量本身是NULL或者0,还是说dlsym()返回错误。

2.6.3 dlerror()

返回值类型是char*,如果返回NULL,则表示上一次调用成功;如果不是,则返回相应的错误消息。

2.6.4 dlclose()

dlclose()的作用跟dlopen()刚好相反,它的作用是将一个已经加载的模块卸载。系统会维持一个加载引用计数器,每次使用dlopen()加载某模块时,相应的计数器加一;每次使用dlclose()卸载某模块时,相应计数器减一。只有当计数器值减到0时,模块才被真正地卸载掉。卸载的过程跟加载刚好相反,先执行“.finit”段的代码,然后将相应的符号从符号表中去除,取消进程空间跟模块的映射关系,然后关闭模块文件。

2.7 共享库构造和析构函数:
  • _attribute_((constructor)):指定该函数为共享库构造函数,拥有这种属性的函数会在共享库加载时被执行,即在程序的main函数之前执行。如果我们使用dlopen()打开共享库,共享库构造函数会在dlopen()返回之前被执行。如果有多个这样的构造函数,那么可以指定优先级来决定函数执行的先后顺序,否则多个构造函数的执行顺序不定。指定优先级:
void \__attribute\__((constructor(n))) innit_func1(void);

对于构造函数来说,n越小函数的优先级越大

  • _attribute_((destructor)):指定该函数为共享库析构函数。这种函数会在main()函数执行完毕之后执行(或者是程序调用exit()时执行)。如果使用dlclose来卸载共享库时,析构函数会在dlclose()返回之前执行。如果有多个这样的析构函数,那么可以指定优先级来决定函数执行的先后顺序。
void \__attribute\__((destructor(n))) finit_func1(void);

对于析构函数来说,刚好相反,n越大函数的优先级越大,这样有利于与构造函数一一对应(数字对应)。

注意:

如果我们使用了这种构造或析构函数,那么必须使用系统默认的标准运行库和启动文件,即不可以使用GCC的“-nostartfiles”或“-nostdlib”这两个参数。因为这些构造和析构函数是在系统默认的标准运行库或启动文件里面被运行的,如果没有这些辅助结构,它们可能不会被执行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

动态链接 的相关文章

  • 单链表排序操作

    单链表排序操作 单链表是常见的一种数据结构 它由一系列节点组成 每个节点包含一个数据元素和一个指向下一个节点的指针 在实际开发中 我们经常需要对单链表进行排序操作 以满足不同的需求 在进行单链表的排序操作时 我们可以采用多种方法 下面将介绍
  • 10个值得前端收藏的CSS3动效库(工具)

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 现在的网站和App的设计中越来越重视用户体验 而优秀的动效则能使你的应用更具交互性 从而吸引更多用户的使用 我一般会在网站中加入一些简单而一致的动效 我所用的技术则是用SA
  • Python 3.打开摄像头,保存AVI视频 OpenCV Linux

    import cv2 import numpy as np from matplotlib import pylab as plt img cv2 imread pic1 png 2 plt imshow img cmap gray int
  • C练题笔记之:Leetcode-565. 数组嵌套

    题目 索引从0开始长度为N的数组A 包含0到N 1的所有整数 找到最大的集合S并返回其大小 其中 S i A i A A i A A A i 且遵守以下的规则 假设选择索引为i的元素A i 为S的第一个元素 S的下一个元素应该是A A i
  • 栈的 创建,入栈,出栈,清空栈,遍历栈 的实现

    数据结构 的学习视频 https www bilibili com video av6159200 from search seid 6709590585276522157 一 算法 栈 数据进出 类向箱子放东西和拿东西 先进后出 或者说后
  • OpenGL Vertex Buffer Objects(VBOs)

    OpenGL Vertex Buffer Objects VBOs 分类 OpenGL2010 05 20 12 53 3714人阅读 评论 13 收藏 举报 buffer float list struct 存储 工作 原创文章转载请注明
  • python 异常之 ValueError: invalid literal for int() with base 10: ‘xxx‘

    文章目录 1 异常例子 2 源代码 3 int x base 的正确使用方法 1 异常例子 代码 if name main print int 123 print int aaa 执行代码 2 源代码 可以看到 在执行 print int
  • LINUX 防火墙iptables常用指令

    封单个IP的命令 iptables I INPUT s 124 115 0 199 j DROP 封IP段的命令 iptables I INPUT s 124 115 0 0 16 j DROP 封整个段的命令 iptables I INP
  • “字符串的展开”【题解】

    字符串的展开 的题目 题目 题目描述 在初赛普及组的 阅读程序写结果 的问题中 我们曾给出一个字符串展开的例子 如果在输入的字符串中 含有类似于 d h 或者 4 8 的字串 我们就把它当作一种简写 输出时 用连续递增的字母或数字串替代其中
  • golang 读取yaml配置文件中的数据 两种方式:yaml.v2 和 Viper

    golang 读取yaml配置文件中的数据 yaml 配置文件 config yaml 中 写数据 app host 127 0 0 1 port 3306 username admin password admin log suffix
  • 110. 平衡二叉树

    给定一个二叉树 判断它是否是高度平衡的二叉树 本题中 一棵高度平衡二叉树定义为 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 Definition for a binary tree node public class Tr
  • VScode设置字体大小

    VScode如何设置字体大小 第一步 首先打开vscode 在vscode的左下角有一个设置按钮 单机打开 选择settings选项 第二步 在设置中查找font 字体 选项 并打开 第三步 在font选项内 选择font size 就可以
  • jQuery验证码插件:jquery.idycode.js

    对于任何一个又评论功能的网站来说 验证码都是重中之重 没有验证码的话 用户就可以肆意刷评论 甚至是通过一些工具来操作 会对网络环境产生极大的危害 验证码这个词最早是在2002年由卡内基梅隆大学的路易斯 冯 安 Manuel Blum Nic
  • 标识符和关键字应该如何理解?

    思考 为什么语言中需要关键字和表示符 程序来源于生活 想想我们人类在生产生活过程中的一些语言使用都有其特定的含义 而每个事物或者事物的一些属性功能也都需要给予特定的语言符号来表示 故java语言的发明者们按照人类的方式创造除了一门值得大家学
  • 分布式、微服务概念

    目录 1 目前软件架构大致分类 2 各种架构技术方法 3 什么是微服务 4 微服务架构特点 5 什么是SOA 6 SOA架构特点 7 SOA架构和微服务架构的区别 8 ESB和微服务API网关 9 什么是分布式 10 什么是集群 11 负载
  • R语言使用cumsum函数计算向量数据的累加和(cumulative sum )

    R语言使用cumsum函数计算向量数据的累加和 cumulative sum 目录 R语言使用cumsum函数计算向量数据的累加和 cumulative sum
  • glsl version 300es 关键字

    参考链接 GLSL ES Specification 3 00 变量名 不能要以gl 开头 注释 或 关键字 void float int uint bool void function name float var name 1 uint
  • JS混淆技术探究及解密方法分析

    随着Web技术的快速发展 JavaScript被广泛应用于网页开发 移动应用开发等领域 然而 JavaScript代码很容易被反编译 解密 这给保护网站和应用程序的安全性带来了严重的挑战 为了解决这个问题 JS混淆技术应运而生 JS混淆就是

随机推荐

  • Redux持久化插件-解决刷新页面数据丢失问题

    最近在使用react的时候有用到redux 对数据进行全局的状态管理 但是发现和vuex一样会出现刷新之后数据丢失的问题 于是在github上面查阅了 redux persist 插件 使用redux persist进行持久化数据存储 通常
  • Rstudio更换主题/样式

    github项目地址 https github com gadenbuie rsthemes 安装 在 rstudio 的控制台console中数据 install packages devtools devtools install gi
  • 为什么会说:程序员年龄越大,越容易失业?

    在程序员的世界里 一直有一个传言 互联网公司没有35岁以上的中年人 从华为辞退34岁以上的员工 到腾讯辞退35 高级员工 似乎老程序员面临着 年龄危机 要时刻警惕在职场被 踢出 的危险 而国内其他很多职业 比如教师 医生 公务员 都在稳步发
  • python TCP通信雷达实时解析数据

    雷达解析程序 coding cp936 import socket import re class jiema def yushe3 self receve r receve av receve v receve h while True
  • 05-分布式计算框架

    目录 一 MapReduce 1 简介 2 原理 2 1 基本概念 2 2 程序执行过程 2 3 作业运行模式 二 Spark 1 简介 1 1 背景 1 2 概念 1 3 特点 2 原理 2 1 编程模型 2 2 运行模式 2 3 运行过
  • Java往字符串数组中追加一个数据

    public class Test public static void main String args 原字符串数组 String arr 原字符串数据1 原字符串数据2 执行数据添加 arr insert arr 需要追加的字符串数据
  • 三菱无机房电梯故障代码查询_【学习】 三菱J、A、K型扶梯介绍(上)

    导 读 抱前段时间我们一直在学习三菱直梯的介绍 今天就为大家分享三菱三种扶梯的介绍 分别为J型 K型和A型 三种扶梯的扶手驱动系统分别是 A型J型为直线式扶手驱动 K型自动扶梯采用摩擦轮式扶手驱动系统 当扶梯的驱动方式不相同时 我们维修的方
  • 解决docker启动mysql无法输入中文以及中文不显示或乱码问题

    前言 我在使用MySQL时 遇到了两个问题 一是在插入中文数据时 无法输入中文 二是在select的时候 查出来的中文数据是空的 因为插入时为空 然后我就使用Navicat连接数据库添加了中文数据 再到docker中查询 就发现了乱码问题
  • LeetCode64. 最小路径和

    题目大意 求出从网络左上角到右下角的一条代价最小的路径和 题目分析 使用动态规划 求出左上角到网络中每个点的代价最小路径和 假设当前要求的是point i j 点 那么它的值就应该是从左上角到它上面那个点point i 1 j 的路径和 与
  • 【技术应用】Qt Creator使用体会与小技巧

    Qt Creator是Qt官方的IDE 这个IDE为Qt编程人员提供了一个完整的开发环境 当然了 这个IDE是用Qt写的 也是免费的 这个IDE真正的编译部分使用了MinGW gcc compiler 也就是说 这个IDE主要的作用是协助开
  • 教务管理系统(免费源码获取)

    项目介绍 本系统使用springboot mybatis plus shiro lombok等技术 使用json传递数据 使用加盐加密对数据进行保存 前端页面使用vue搭建并打包放在static文件夹中 使用token保存当前用户 当用户登
  • chrome浏览器network报错:ERR_CERT_AUTHORITY_INVALID

    转载请注明作者 独孤尚良dugushangliang 出处 https blog csdn net dugushangliang article details 85275319 在访问局域网的某网址时 提示 您的连接不是私密连接 错误代码
  • 算法在ros中应用_烟火检测算法——中伟视界人工智能算法AI在智慧工地、石油中的应用_腾讯新闻...

    烟火检测算法功能说明及实现原理等 一 软件概述 视频智能分析基于目前先进的深度学习算法 通过大量的项目现场素材训练模型 通过本站大量采集的工作服素材 高精度的识别人 安全帽 工作服等识别 本项目主要两方面的算法 一是识别类的 二是行为分析
  • WPF中Datagrid其中一列使用图片显示

    实现效果 实现遇到的问题 当时想要实现如图所示 合格率 所示的效果 我的第一个想法就是使用wpf的转换器 可是接下来问题来了 我这个是通过数值来判断是否合格 什么控件可以做到既可以绑定图片类型的 又可以绑定数值类型的 还有此时的当值绑定肯定
  • 段、页、页框、页表、页表项

    段 页 页框 页表 页表项 分页式虚拟内存 页 页框 页表 页表项 段页式虚拟内存 分段 分页 段 段表 段表项 页 页框 页表 页表项 分页式虚拟内存 页 页框 页表 页表项 页 进程中的块 进程被分成许多大小相同的块 页号 页框 内存中
  • TS2769: Property 'xxx' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttribute...

    用TypeScript开发React项目 在父子组件间传值时发生错误提示 class Page extends React Component render return div div
  • vue组件利用css var(--变量)实现动态修改伪类属性(::before、::after)

    如图所示 1 我们可以利用此属性实现vue组件动态传值 修改例如 before after等 伪类的背景色 背景图等属性值 因为vue利用无法直接在css中使用data里的变量 利用var 变量名 以及style中定义变量 其实此步是模仿
  • Coordinate attention,SE,CBAM

    1 SE 因为普通卷积难以建模信道关系 SE考虑通道的相互依赖关系增强模型对信息通道的敏感性 同时全局平均池化可以帮助模型捕获全局信息 然而SE只考虑了内部通道信息而忽略了位置信息的重要性 输入X首先经过全局平均池化 然后经过全连接层来捕获
  • 静态类和动态类的区别和使用

    1 静态类中的静态方法可以通过类名直接调用静态方法 不需要实例化对象 但是无法和Spring容器中的bean进行交互 例如 Slf4j public class ExcelUtil public static
  • 动态链接

    动态链接 命令 gcc static 产生静态库 shared 产生共享库 readelf d 查看 dynamic段的内容 ldd 查看一个程序主模块或一个共享库依赖于哪些共享库 一 静态链接和动态链接的优缺点 静态链接 空间的浪费 静态