C++学习:第六章Linux高级编程 - (二)映射虚拟内存、gcc、静态库、动态库、make

回顾:
    brk/sbrk
    int brk(void *p);
    void *sbrk(int);
    维护一个位置。brk/sbrk改变这个位置
       brk改变绝对位置。
        sbrk相对改变位置。
补充:全新的类型。
    永远记住:c的基本类型就:整数(1字节,2字节,4字节),小数(4字节,8字节)
        unsigned signed
    所有全新类型都是使用typedef重新定义。学会找到新的类型的C的原型。
    类型重定义的好处:
        1. 维护方便
        2. 移植
        3. 容易理解

一、 映射虚拟内存


    没有任何额外维护数据的内存分配。
    mmap(分配)/munmap(释放)

    1. 函数说明
        void *mmap(
            void *start,    //指定映射的虚拟地址,0表示由系统指定开始位置)
            size_t length,    //映射空间大小 pagesize倍数
            int prot,        //映射权限  PROT_NONE | PROT_READ PROT_WRITE PROT_EXEC
            int flags,        //映射方式
            int fd,        //文件描述符号
            offset_t off);    //文件中的映射开始位置(必须是pagesize的倍数)

    映射方式:
        内存映射:匿名映射。
        文件映射:映射到某个文件
            只有文件映射最后两个参数有效。
            MAP_ANONYMOUS 内存映射方式
            MAP_SHARED   MAP_PRIVATE(二选一)
    2. 案例

#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+2)=40;
    
    printf("%d\n",p[2]);
    munmap(p,4096);//输出40
}

    3. 总结
        选择什么样的内存管理方法?
            智能指针(指针池)
            STL
            new
            malloc     (小而多数据) 
            brk/sbrk     (同类型的大块数据,动态移动指针)
            mmap/munmap(控制内存访问/使用文件映射/控制内存共享)        
    4. 应用

二、 编程工具与动态库


    1.gcc
        -o    输出文件名
        -O  -O0 -O1 -O2 -O3    编译优化
        -g  -g0 -g1 -g2 -g3        产生调试信息
        -W  all  error   
        -Wall        显示所有警告
        -Werror        把警告当错误
        -w    关闭警告

        -c    只编译不连接
        -E    预编译
        -S    汇编

        -D    在命令行定义宏

        -x    指定编译的语言类型
            c++ / c / .S / none 自动判定
        -std=C89 / C99

    编译过程:-E  -c  -S    自动调用连接器
    连接器    ld
补充:
    .c            C程序
    .cpp        C++程序
    .CC        C++程序
    .h            头文件
    .hpp        C++里面的头文件
    .o            目标文件
    .a            归档文件 | 静态库文件
    .so          共享文件 | 动态库文件
    .i            预编译文件
    .s            汇编文件

三、 静态库的编译


    1. 编译过程(得到*.a文件,即achieve)
        1.1. 编译成目标文件
            -static    可选
            gcc -c -static 代码文件.c  //得到.o文件
        1.2. 归档成静态库
            ar工具
                ar -r        在目标文件中插入静态库
                ar -t        显示归档文件中的内容
                ar -r        静态库文件    被归档的文件  ar -r ku.a ku1.o ku.o 得到ku.a文件

            nm工具(察看函数符号表)
                nm 静态库 或者 动态库 或者 目标文件 或者 执行文件
        1.3. 使用静态库
            gcc 代码  静态库
            gcc diaoyongku.c ku.a -omain

            使用静态库完成如下程序:
                输入一个菱形半径,打印菱形。
                输入整数封装成IOTool
                菱形的打印封装成Graphics

                注意:静态库在 gcc diaoyongku.c ku.a -omain 后已经将静态库代码写入到主文件中,因此生成程序可以直接运行,不需要配置任何参数。

        1.4 总结:
            1.4.1 什么是库?
                函数等代码封装的二进制已经编译的归档文件
            1.4.2 ar归档工具
            1.4.3 采用库的方式管理代码优点:
                容易组织代码
                复用
                保护代码版权
            1.4.4 静态库的静态的含义:
                编译好的程序运行的时候不依赖库。
                库作为程序的一部分编译连接。
            1.4.5 静态库本质:
                就是目标文件集合(归档)
            1.4.6 -static可选

    2. 库的规范与约定
        2.1 库命名规则:
            lib库名.a.主版本号.副版本号.批号
            lib库名.a
        2.2 库使用规则
            -l 库名
            -L 库所在目录
            gcc main.c -omian -lku -L.    //调用的ku.a文件,尾缀省略了。指定库 ku.a所在的目录:当前目录

四、 动态库的编译


    1. 什么是动态库?(共享库)
        动态库是可以执行,静态库不能执行。
        动态库没有main,不能独立执行。
        动态库不会连接成程序的一部分。
        程序执行的时候,必须需要动态库文件。
    2. 工具
        ldd        察看程序需要调用的动态库,只能察看可执行文件(共享库或者执行文件*.elf)。
        ldd  mian    //main函数在执行过程中调用了几个动态库
        ldd        
        readelf  -h  mian  察看执行程序头.
        nm            察看库中的函数符号
    3. 动态库的编译
        3.1 编译
            -c -fpic(可选)
            gcc  -c  -fpic  iotool1.c //编译得到目标.o文件
            gcc  -c  -fpic  iotool2.c //编译得到目标.o文件
        3.2 连接
            -shared
            gcc  -shared  -odemo.so  iotool1.o  iotool2.o//得到demo.so动态库文件
    4. 使用动态库
        gcc  代码        动态库文件名(这里包含尾缀和lib头)
        gcc  代码        -l库名    -L动态库所在路径
        gcc  mian.c  -ldemo  -L.  -omian
            注:这里的使用只是完成了外部的链接与调用,即完成了编译封装工作,但是执行程序还是无法运行。这里注意,程序运行不是按照编译封装时动态库的路径去查找动态库,并加载运行的,而是有单独的机制。编译链接时只是指定主程序调用动态库中函数所在动态库中的位置,编译成功的文件在执行时还需要一个地址映射到该动态库文件的文位置,这里一般是内存中的地址映射,系统会根据动态库加载的目录先把动态库映射到内存中,在程序运行时直接去内存中访问各动态库,以提高速度。见6、7。

    5. 标准命名规则:
        lib库名.so    动态库
        lib库名.a        静态库
        -l 库名  -L 库所在路径

    6. 问题:
        6.1 执行程序怎么加载动态库?
    见7
        6.2 动态库没有作为执行程序的一部分,为什么连接需要指定动态库以及目录?
            连接器需要确定函数在动态库的中的位置

    7. 动态库的加载:
        7.1 找到动态库
        7.2 加载动态库到内存
        7.3 映射到用户的内存空间
            系统对动态库查找规则:
                /lib
                /usr/lib
                到环境变量LD_LIBRARY_PATH指定的路径中查找
                    export LD_LIBRARY_PATH =.:~:..:~soft01
                    解释:    .     表示当前目录
                                    ..    表示上级目录
                                    ~    表示主目录
                                    :     表示分隔符
                        ~soft01     表示指定soft01用户的主目录

                缓冲机制:
                    把/lib:/usr/lib:LD_LIBRARY_PATH加载到缓冲。
                    /sbin/ldconfig -v 刷新缓冲中so的搜索数据


五、 使用libdl.so库


    动态库加载的原理
    动态库中函数的查找已经封装成库libdl.so,这就是为啥要用它
    头文件 #include <dlfcn.h>
    dlopen        打开一个动态库
    dlsym          在打开动态库找一个函数
    dlclose        关闭动态库
    dlerror         返回错误

#include <dlfcn.h>
main()
{
    void *handle=dlopen("./libdemo4.so",RTLD_LAZY);
    void(*fun)(int)=dlsym(handle,"diamond");
    fun(5);
    dlclose(handle);
}

    说明:
        1. 图片中红线部分可省略,因为是系统的库,所以在系统目录及内存中已有映射,所以无需再指定。
        2. 为什么需要调用libdl.so系统动态库文件?因为程序在执行过程中要用到,同时作为演示系统动态库如何使用的作用。
        3. 为什么不用添加libdemo4.so动态库文件?因为在代码中用另一种方式导入了已经。

六、 总结:


    6.1 编译连接动态库
    6.2 使用动态库
    6.3 怎么配置让程序调用动态库
    6.4 掌握某些工具的使用:nm ldd lddconfig
        objdump        对二进制文件进行输出
            strip            去掉多余的信息.

七、 工具make的使用与makefile脚本


    1. 背景:
        make    编译脚本解释,通过脚本文件替代gcc
        编译的脚本叫makefile
        make -f  脚本文件  目标

    2. 脚本文件
        2.1 文本文件 demo.mk
        2.2 基本构成语法:
            基本单位:目标target
            目标名:依赖目标
               \t(这是Tab键,文件的格式要求)目标指令1
               \t目标指令2

        demo:
                gcc iotool.c -c 
                gcc graphic.c -c
                gcc iotool.o  graphic.o -shared -olibdemo.so
                gcc main.c -ldemo -omain -L.

依赖调用,空格表示多依赖
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值