嵌入式Linux支持dlopen,《嵌入式Linux内存与性能详解》笔记3——动态库内存优化...

本文深入探讨了嵌入式Linux系统中动态库的内存优化,包括动态库的代码段和数据段处理,强调了动态库数据段对进程内存的影响。文章通过实例分析了动态库数据段的优化策略,如减少无用动态库、合并动态库、控制动态库的生存周期等,旨在提高系统性能和内存利用率。
摘要由CSDN通过智能技术生成

一、前言

在编程中,我们有时会使用到静态库和动态库,而静态库是链接到程序之中的,基本上静态库的优化与进程类似。但动态库是加载在内存空间中的,是在运行时链接的。它的优化方式比较不同,本文我们就简单地讲述一下动态库如何优化

二、动态库

对于 动态库 而言,它仅包括 2 个段:

只读的代码段

可修改的数据段。

堆段 和 栈段 只有进程中才有。所以,如果你在动态库的函数里分配了一块内存,这段内存将被算在调用该函数的进程的 堆段 中。

系统在处理 动态库 的 代码段、数据段 时和 程序 的 代码段、数据段 一样:

代码段 由于其内容是对每个进程都是一样的,它在系统中是唯一的,系统只为其分配一块内存,多个进程之间共享。

数据段 由于其内容对每个进程是不一样的,所以在链接到进程空间后,系统会为每个进程创建相应的数据段。

2.1、BSS段

在 进程中 的 BSS 会被开辟在 堆段 中。但在 动态库 中则是另外开辟一段 内存段 来存放这些数据,我们看下面的代码:

/* test.c */

#include

#include

int bss[1024*128]={0};

void funca()

{

printf("bss:%p\n",bss);

return;

}

/* main.c */

#include

#include

#include

#include

#include

#include

#include

int funca();

int main()

{

int pid=getpid();

printf("pid:%d\n",pid);

funca();

pause();

return 0;

}

动态库编译: arm-linux-gnueabihf-gcc -shared -fPIC test.c -o libtest.so

主程序编译:arm-linux-gnueabihf-gcc -L./ -ltest main.c -o main

编译完成后记得把 动态库和主程序 一起拷贝到嵌入式设备上,其中具体的动态库查找路径相信各位读者都有些许了解,我们运行后得到下面的结果:

00f1ee273956

运行结果

00f1ee273956

smaps.png

可以看到,我们的进程中有专门的内存段来存放 动态库 的数据,可以看到其中有 2 个段各自占据了 1 个物理页

再看看如果在进程中引用动态库中的数据的情况,代码如下:

/* test.c */

#include

#include

int bss[1024*128]={0};

void funca()

{

printf("bss:%p\n",bss);

return;

}

/* main.c */

#include

#include

#include

#include

#include

#include

#include

extern int bss[1024*128];

int funca();

int main()

{

int pid=getpid();

printf("pid:%d\n",pid);

funca();

pause();

return 0;

}

00f1ee273956

运行结果

00f1ee273956

smps

我们发现 地址不在动态库的段中,而是在程序的堆栈里面。这样来看,即使我们没使用该数据,但还是会占用我们的物理页。所以我们尽量少引用动态库中的全局变量。一旦引用,不论其是否使用,都将会占用物理内存。同时还会增加系统启动时,内存复制的代价,会导致性能的下降。

2.1、动态库数据段对进程的影响

上一小节说了,如果全局变量声明在共享库中,在进程中使用,则该变量被复制到进程的数据段, 同时修改使用该变量的指向。 那么接下来看看 动态库 的 数据段 会对 进程 的 数据段 产生什么影响?

我们通过 3 个实例来验证这个问题,每个实例都分别给出:代码、运行结果、文件大小、堆段信息、动态库数据段信息

声明在bss段,但在进程中使用

/* test.c */

#include

#include

int bss[1024*8]={0};

void funca()

{

printf("dynamic bss = %p\n",bss);

return ;

}

/* main.c */

#include

#include

#include

#include

#include

#include

#include

extern int bss[1024*8]; //32KB

int* funca();

int main()

{

int pid=getpid();

int i = 0;

funca();

for(i = 0; i < 1024*8; i++)

bss[i] = 99;

printf("program bss[0] = %d\n",bss[0]);

printf("pid:%d bss:%p\n",pid,bss);

pause();

return 0;

}

00f1ee273956

运行结果

00f1ee273956

文件大小

00f1ee273956

堆段信息

00f1ee273956

动态库数据段信息

我们可以看到,在 动态库的数据段 中并没有占用内存,仅仅是在 进程 的 堆段 中占用内存。

声明在bss段,但在进程中不使用

/* test.c */

#include

#include

int bss[1024*8]={0};

void funca()

{

printf("dynamic bss = %p\n",bss);

return ;

}

/* main.c */

#include

#include

#include

#include

#include

#include

#include

extern int bss[1024*8]; //32KB

int* funca();

int main()

{

int pid=getpid();

int i = 0;

funca();

printf("pid:%d bss:%p\n",pid,bss);

pause();

return 0;

}

00f1ee273956

运行结果

00f1ee273956

文件大小

00f1ee273956

堆段信息

00f1ee273956

动态库数据段信息

该实例在主程序中引用了 动态库的数据,但没有修改数据的内容。可以看到其结论与第一个实例相同,也就是说明 在动态库中的数据一旦被主程序引用,那么主程序就会在堆段开辟内存并将动态库中的内容拷贝到堆段内存中

声明在data段

/* test.c */

#include

#include

int bss[1024*8]={1};

void funca()

{

printf("dynamic bss = %p\n",bss);

return ;

}

/* main.c */

#include

#include

#include

#include

#include

#include

#include

extern int bss[1024*8]; //32KB

int* funca();

int main()

{

int pid=getpid();

int i = 0;

funca();

printf("program bss[0] = %d\n",bss[0]);

printf("pid:%d bss:%p\n",pid,bss);

pause();

return 0;

}

00f1ee273956

运行结果

00f1ee273956

文件大小

00f1ee273956

堆段信息

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值