zoukankan      html  css  js  c++  java
  • C 高级编程3 静态库与动态库

    http://blog.csdn.net/Lux_Veritas/article/details/11934083
    http://www.cnblogs.com/catch/p/3857964.html


    mmap/munmap工具使用 #include <unistd.h> #include <sys/mman.h> #include <stdlib.h> #include <stdio.h> main() { int *p=mmap(NULL, getpagesize(), //页的大小 PROT_READ|PROT_WRITE, //读写权限 MAP_ANONYMOUS|MAP_SHARED,0,0); *p=20; *(p+1)=30; *p(p+2)=40; printf("%d ",p[2]); munmap(p,4096); //释分配的页 } 40 -------------------------------------- #include <sys/mman.h> #include <stdlib.h> #include <stdio.h> main() { int *p=mmap(NULL, getpagesize(), PROT_READ, //测试只读权限 MAP_ANONYMOUS|MAP_SHARED,0,0); *p=20; *(p+1)=30; *p(p+2)=40; printf("%d ",p[2]); munmap(p,4096); //释分配的页 } 段错误 ------------------------------------------------------ 总结: 选择择什么样的管理方法 C++: STL :操作方便 new :操作方便 智能指针 C: malloc :小而多数据(类型多,数据多) brk/sbrk : (同类型的数据,动态移动指针) mmap/munmap: 控制内存的访问权限,使用文件映射,控制内存的共享(进程数据间共享) brk/sbrk,mmap/munmap 效率最高的 ------------------------------------------------------------------- 编程工具与动态库 1.GCC 2.MAKE 3.GDB 4.其他工具 5.共享库 gcc: -o: 输出的文件名 -O,-O0,-O1,-O2,-O3 :编绎优化 -g,-g0,-g1,-g2,-g3 :产生调试信息 越来越大,调试信息越多 -W :取消警告 -Wall 显示所有警告 -Werror:把警告当错误 -c :只编绎不链接: -E :预编绎 gcc map.c -E -omain.i -S :汇编 编绎过程:-e -> -c ->-S :自动调用连接器 连接器 :ld -D 在命令行定义宏 在代码中定义宏 在命令令行定义宏 -x 指定编译的语言类型号 c++,c,s,none gcc -x assembler map.s gcc -x none map.c //自动 -std: c99,c89 gcc -std=c99 gcc.c -DNUM=45 gcc -std=c89 gcc.c -DNUM=45 ---------------------------------- eg: gcc.c int printf(const char *,...); main() { printf("%d ",NUM); } gcc gcc.c -omain -DNUM=56 //在命令定义宏 EG2 int add(int * restrict a) restrict:值放到寄存器,提搞速度,c99可以编绎通过,c89不可以 ----------------------------------- 文件扩展名: .c: c文件 .cpp:c++ .CC:c++ .h 头文件 .hpp 头文件 .a .o 目标文件 .so :动态链接库 .i 预编绎文件 .s 汇编 示例: gcc map.c -omain -O gcc map.c -omain -O0 gcc map.c -omain -O1 gcc map.c -omain -O2 gcc map.c -omain -O3 gcc map.c -omain -w gcc map.c -omain -Wall gcc map.c -omain -Werror ---------------------------------------------- 三 .静态库的编绎 1.编绎过程(*.a) 1.1.编绎成目标文件 -static 可选 gcc -c -static <code> 1.2.归档成静态库 ar工具 ar -r 静态库文件 被归档的文件 ar -r ku.a ku1.o ku2.o ar -t ku.a //查看有那些目标文件 nm工具:(查看函数符号表) nm ku.a nm 表态库/动态库/目标文件/执行文件 总结: 什么是库? 1.函数封装的的二进制已经编译的归档中 2.ar归档工具 3.才用库方式管理代码优点 容易组织代码 复用 保护代码版 4.静态库的静态的含义: 编译好的程序运行时候不依赖库 库作用为程序的一部分编译连接 5.表态库本质: 就是目标文件的集合(归档文件) 6.-static 可选 2.库的规范与约定 库命令名规则 lib<库名>.a.主版本号.次版本号.批号 lib<库名>.a eg: ar -r libdemo1.a optool.o graphic.o 库的使用规则:标准使用方法 -l:库名 -L:库目录 gcc main.c -omain -ldemo2 -L. //静态库 //在当前目录搜索libdemo2.a这个文件 3. eg: ku1.c int add(int ,a int b) { return a+b; } ku2.c int sub( int a,int b) { return a-b; } callku.c main() { int r=add(45,55); int s=sub(100,45); } gcc -static -c ku1.c ku1.o gcc -static -c ku2.c ku2.o ar -r ku.a ku1.o ku2.o gcc callku.c ku.a -omain //非标准库调用 //ku.a静态库 ----------------------------------------------------------- 四.动态库的编绎 1.什么是动态库?(共享库) 动态库是可以执行,静态库不能执行 但动态库没有main,不能独立执行 动态库不会连接成程序的一部分 程序执行时候,必须需要动态库文件 2.工具 ldd 察看程序使用的动态库(只可以查看可执行文件) eg: ldd main nm 查看库中的函数符号 readelf -h main 查看执行程序头信息 elf格式 [root@monitor ~]# readelf -h a.out ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x400500 Start of program headers: 64 (bytes into file) Start of section headers: 2984 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 8 Size of section headers: 64 (bytes) Number of section headers: 30 Section header string table index: 27 3.动态库的编译 3.1.编绎 -c -fpic(可选) gcc -c -fpic iotool.c gcc -c -fpic graphic.c 1.gcc -shared -odemo3.so iotool.o graphic.o 非标准动态库名 2.gcc -shared -olibdemo4.so iotool.o graphic.o 标准动态库名 4使用动态库 gcc 代码 动态库文件名 gcc code -l库名 -L动态库所在路径 3.2 gcc -c -fpic iotool.c gcc -c -fpic graphic.c gcc -shared -olibdemo4.so iotool.o graphic.o gcc main.c -ldemo4 -L. -omain ldd main 结果: linux-gate.so.1 libdemo4.so=>not found libc.so.6 /lib/ld-linux.so.2 ./main报错 解决方法: export LD_LIBRARY_PATH=.:~:..:~soft01 //只对当前shell有效 表示到这些目录去找 ~soft01:指定soft01用户的主目录 ~ :当前用户主目录 库标准命令名规则: lib库名.so lib库名.a -l 库名 -L 库所在路径 问题: 4.1.执行程序怎么加载动态库? 见(动态库的加载) 4.2.动态库没有作为执行程序的一部分,为什么连接需要指定动态库以及目标录? 连接器需要确定函数在动态库的偏移的位置 动态库的加载: 1.找到动态库(系统对动态库查找规则) 2.加载动态库到内存 3.映射到用户的内存空间 系统对动态库查找规则 1./lib :需要ROOT权限把动态库考到该目录 2./usr/lib :需要ROOT权限把动态库考到该目录 3.到环境变量LD_LIBRARY_PATH指定的路径中查找 如果没有root 权限,可以在LD_LIBRARY_PATH指定的路径中查找 export LD_LIBRARY_PATH=.:~:..:~soft01 缓冲机制: 把/lib:/usr/lib:LD_LIBRARY_PATH加载到内存缓存中,加速读取,不到硬盘查找 /sbin/ldconfig -v 刷新缓冲中so的搜索路径 /lib,/usr/lib eg: /sbin/ldconfig -v |grep "libdl.so" -v: 用此选项时,ldconfig将显示正在扫描的目录及搜索到的动态链接库,还有它所创建的连接的名字. 5.使用libdl.so 库 位置:/usr/lib/libdl.so 动态库加载原理 http://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/ ----------------------------------------------------------- [root@monitor ~]# gcc test.c -m32 -o test [root@monitor ~]# ldd test linux-gate.so.1 => (0x00d28000) libc.so.6 => /lib/libc.so.6 (0x00194000) //C语言标准库 /lib/ld-linux.so.2 (0x00ba3000) [root@monitor ~]# /lib/ld-linux.so.2 ./test ----------------------------------------------------------------- [root@monitor ~]# gcc test.c -o test [root@monitor ~]# ldd test linux-vdso.so.1 => (0x00007fff629ff000) libc.so.6 => /lib64/libc.so.6 (0x0000003c4ec00000) //C语言标准库 /lib64/ld-linux-x86-64.so.2 (0x0000003c4e400000) [root@monitor ~]# /lib64/ld-linux-x86-64.so.2 ./test ------------------------------------------------------------------ 动态库中函数的查找已经封装成库libdl.so,用户写代码加载动态库 dlopen 打开一个动态库 (flag:RTLD_LAZYRTLD_NOW) dlsym 在打开的动态库中找一个函数 dlclose 关闭动态库 dlerror eg: dldemo.c :动态库用代码加载加载演示 #include <dlfcn.h> main() { void *handle=dlopen("./libdemo4.so",RTLD_LAZY); void (*fun)(int)=dlsym(handle,"diamond"); fun(5); dllclose(handle); } gcc -dldemo.c -omain -ldl -L/usr/lib 没有指定-ldemo4 gcc -dldemo.c -omain -ldl //-L/usr/lib 不指定也可以 ./main ldd main nm libdemo4.so 查函数列表 man: SYNOPSIS #include <dlfcn.h> void *dlopen(const char *filename, int flag); char *dlerror(void); void *dlsym(void *handle, const char *symbol); int dlclose(void *handle); Link with -ldl. 总结: 编绎连接动态库 使用动态库 怎么配置于让程序调用动态库 常握工具:nm ldd lddconfig objdump strip eg: strip libdemo4.so 删除多的信息 nm libdemo4.so 6.工具make使用 make 编绎脚本解释 编译脚本Makefile make -f 脚本文件 目标 脚本文件: 1.文本文件 demo.mk 2.语法 基本单位目标target 目标名:依赖目标 ab目标指令 ab目标指令 ab目标指令 ab目标指令 ... -------------------------------------------------- demo.mk --------------------------------------------------- demo:iotool.c graphic.c main.c gcc iotool.c -c //以tab健开始 gcc graphic.c -c //以tab健开始 gcc iotool.o graphic.o -shared -olibdemo.so //以tab健开始 gcc main.c -ldemo -omain -L. //以tab健开始 ---------------------------------------------------- make -f demo.mk demo ---------------------------------------------------- ./main ---------------------------------------------------- ldconfig命令: ldconfig为在命令行中说明的目录或文件/etc/ld.so.config中指定的目录或一些可信任的目录 (象/usr/lib, /lib)中的最新的动态连接库创建必要的连接和绶存。这些绶存起来的数据会被动态 连接器ld.so 或 ld-linux.so所使用。ldconfig会检查它所遇到的动态库文件的名称与版本号, 以决定那些动态库的连接要进行更新。 ldconfig会尝试推断ELF类型库(象libc5, libc6/glibc)是基于那一个版本的C库的。当然,在创建 动态库的时候,最好是明确的指定出使用C库(使用 -lc) 一些已经存在的动态库的能提供的信息,不足以使ldconfig来推断出它们的类型。因此,/etc/ld.so.config 文件格式允许进行明确的说明。这只是对于我们不能计算出类型的ELF库才有用。文件的格式是"dirname=TYPE", TYPE可以是libc4, libc5, libc6. (这个语法在命令行上也可以使用). 空格是的禁止的。可以 参见-p 选项。ldconfig通常只能由超级管理员使用。因为它可能会修改一些root拥有的目录和文件。 选项: -v 详细模式。打印当前版本,扫描的目录,创建的连接。 覆盖安静模式。 -n 只处理在命令行上指定的目录。不处理可信息目录(/lib, 或 /usr/lib)和在 /etc/ld.so.config 中指定的目录。暗含 -N 选项 -N 不重建绶存。除非使用了 -X 选项,否则连接还是会更新的。 -X 不更新连接。除非使用了 -N 选项,否则绶存还是会更新的。 -f conf 使用conf 代替 /etc/ld.so.conf -C cache 使用cache 代替 /etc/ld.so.cache -r root 转换到并使用root -l 库模式。手动连接单个库。 建议专家级才使用。 -p 打印在绶存中的目录和候选库。 文件: /lib/ld-linux.so.2 运行时库加载器 /etc/ld.so.conf 由逗号、空格、制表符、换行符或引号分隔的目录列表。ld将会在这些目录中查找连接库。 /etc/ld.so.cache 包含了在/etc/ld.so.conf中指定的目录中查找到所有连接库。按顺序存储。

    在基于 GNU glibc 的系统上,包括所有 linux 系统,ELF 可执行二进制文件的运行自动导致程序加载器被加载并且运行。

    在 linux 下,加载器是 /lib/ld-linux.so.X(X是版本号)。然后加载器搜索、加载程序所要使用的动态链接库。

    被搜索的文件夹列表保存在文件 /etc/ld.so.conf 里。

    在程序启动的时候搜索这些文件夹是很没有效率的,所以实际上使用缓存。ldconfig(8) 默认读取 /etc/ld.so.conf 文件,在 DLL 文件夹里创建合适的符号链接,在 /etc/ld.so.cache 里写入一个缓存。缓存大大加速了库的读取。所以,当一个 DLL 被添加、删除时,或DLL文件夹被改变时都需要运行 ldconfig 程序,当安装了一个新的 DLL 时,由软件包管理器自动运行 ldconfig。当程序启动时,装载器实际使用的是缓存。

    ------------------------------------------------------------------------------------------------------------------------------------------------------------

    
    

    1. 库的命名习惯

    一个linux DLL 有三个不同名字的文件组成

    soname 文件

    
    
    lib + 链接库名字 + .so + .版本号
    
    

    每当链接库接口改变时都递增版本号。soname 文件其实只是一个符号链接而已,指向他的real name 文件。

    real name 文件

    
    
    lib + 链接库名字 + .so + .版本号.次版本号.发行号
    
    

    发行号是可选的。该文件包含实际代码。

    linker name 文件

    
    
    lib + 链接库名字 + .so
    
    

    编译器以这个名字来请求指定的链接库。

    当程序在内部列出所需要的链接库时,仅仅使用 soname。当你创建一个链接库时,使用 real name。安装一个新的链接库时,把它复制到一个DLL文件夹里,然后运行程序 ldconfig(8)。ldconfig 检查存在的 real name 文件,并且创建指向它的符号链接 soname 文件。ldconfig 还做一件事情就是建立 cache 文件 /etc/ld.so.cache

    ldconfig 不会创建 linker name 文件,但是一般性 linker name 文件在安装链接库的时候创建。linker name 文件也只是一个符号链接,指向最新的 soname 文件或 real name 文件。建议指向 soname 文件,因为当你更新库以后,在编译器链接的时候,一般总是想使用新的库。

    2. 库的放置

    DLL 必须放置在文件系统的指定位置。多数开源软件遵守GNU 标准:当分发源代码的时候,库默认安装在 /usr/local/lib,命令安装在 /usr/local/bin。该标准还定义了如何重写这些默认标准以及如何调用安装程序。

    Filesystem Hierarchy Standard(FHS) 规定:多数库应安装在 /usr/lib,启动时需要的库安装在 /lib,非系统库应安装在 /usr/local/lib

    GNU 标准是针对开发人员的,FHS 是针对发行者的。

    二、 库是如何被使用的

    在基于 GNU glibc 的系统上,包括所有 linux 系统,ELF 可执行二进制文件的运行自动导致程序加载器被加载并且运行。在 linux 下,加载器是 /lib/ld-linux.so.X(X是版本号)。然后加载器搜索、加载程序所要使用的动态链接库。

    被搜索的文件夹列表保存在文件 /etc/ld.so.conf 里。

    在程序启动的时候搜索这些文件夹是很没有效率的,所以实际上使用缓存。ldconfig(8) 默认读取 /etc/ld.so.conf 文件,在 DLL 文件夹里创建合适的符号链接,在 /etc/ld.so.cache 里写入一个缓存。缓存大大加速了库的读取。所以,当一个 DLL 被添加、删除时,或DLL文件夹被改变时都需要运行 ldconfig 程序,当安装了一个新的 DLL 时,由软件包管理器自动运行 ldconfig。当程序启动时,装载器实际使用的是缓存。

    环境变量

    LD_LIBRARY_PATH
    该变量里所指定的文件夹将会首先被搜索,然后才会搜索默认的 DLL 文件夹。该变量对开发和测试比较有用,但不应该为了给普通用户使用而设置。如果你不想设置该变量,在 linux 下你可以直接调用程序加载器,比如,你可以传递 PATH 参数给加载器代替该变量来运行程序:

    
    
    /lib/ld-linux.so.2 --library-path PATH EXECUTABLE
    
    

    不带参数执行加载器,可以得到更多帮助。但是,不要这样执行程序,仅供调试时使用。

    LD_DEBUG
    看名字就知道,是供调试使用的。该变量是dl*函数的开关,用来显示正在做的事情的详细信息。可以取值为:

    
    
    files 显示so文件的加载顺序
    bindings 显示关于符号帮定的信息
    libs 显示库搜索路径的信息
    versions 显示版本依赖的信息
    help 使用该值运行程序将会显示可用的选项
    
    


    三、创建动态链接库

    
    

    首先用 -fpic 或 -fPIC 选项创建要放入 DLL 中的目标文件。使用该选项生成的代码是位置无关的代码(DLL的必要条件)。使用 gcc 的 -Wl 选项传递 soname 参数给链接器。-Wl 选项里不能有未转义的 whitespace。使用如下命令创建 DLL:

    
    
    gcc -shared -Wl,-soname,your_soname /
        -o library_name file_list library_list
    
    

    举个例子:

    
    
    gcc -fPIC -g -c -Wall a.c
    gcc -fPIC -g -c -Wall b.c
    gcc -shared -Wl,-soname,libmystuff.so.1 /
        -o libmystuff.so.1.0.1 a.o b.o -lc
    
    

    该例子首先创建了两个与位置无关的目标文件 a.o、b.o,然后生成了一个包含两者的 DLL。注意:-g 选项使代码包含调试信息,-Wall 选项用来产生警告,两者并不是创建 DLL 必须的,但是建议加上。

    不要 strip 所生成的 DLL,或使用编译器参数 -fomit-frame-pointer,这样做将会无法使用调试器。

    -fPIC 选项总是可以使用,但生成的代码比使用 -fpic 的要大。-fpic 选项生成的代码比较小、快,但是有平台相关的限制,当创建 DLL 时,链接器将会告诉你是否符合限制。

    链接器还有一个有用的选项 -rpath,可以用来指定程序在运行时搜索DLL时的路径,使用 gcc 时可以这样传递参数给链接器:

    
    
    -Wl,-rpath,$(DEFAULT_LIB_INSTALL_PATH)
    
    

    如果你使用了这个选项,就不用考虑 LD_LIBRARY_PATH 这个环境变量了。

    
    
    
    
    

    四、安装、使用动态链接库

    
    

    1.安装在标准位置
    最简单的安装方式是复制 DLL 到一个标准的 DLL 文件夹(/usr/lib等)并且运行 ldconfig(8),然后手动创建 linker name 符号链接。

    2.安装在非标准位置
    下面的命令将会在指定的文件夹里创建适当的 soname 符号链接。

    
    
    ldconfig -n directory_with_shared_libraries
    
    

    然后手动创建 linker name 文件指向 soname 文件。

    
    


    编译程序的时候使用 -l、-L 参数指定需要链接的库和库所在的位置。
    除非使用 -rpath 参数指定过运行时库搜索路径,否则在运行时也必须指定。(所以在eclipse中运行称找不到liqwt.so.5)

    
    

    比如可以使用如下命令添加当前工作目录到 LD_LIBRARY_PATH 来运行程序:

    
    
    LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH  my_program
    
    


    ldd 命令可以用来查看程序的依赖,例如:

    
    
    ldd /bin/ls
    
    输出的是 soname 列表    
    
        
    
        
    
           
  • 相关阅读:
    editplus 快捷键
    python 堆栈
    python 矩阵转置
    python 单向链表
    python 读空的json文件
    c++ 结构体
    手把手教你如何利用Meterpreter渗透Windows系统
    vuejs npm chromedriver 报错
    强大的开源网络侦查工具:IVRE
    在vue 中使用Stylus
  • 原文地址:https://www.cnblogs.com/zengkefu/p/5520079.html
Copyright © 2011-2022 走看看