当前位置:首页 > 公众号精选 > 嵌入式大杂烩
[导读]作者:刘传玺 、盛洁、黄子轩 一. 实验目的及实验环境  1.实验目的 通过编译和链接一个程序,深入理编译和链接都做了什么,并掌握静态库和动态链接库的编写和调用方法。 2.实验环境      (1)硬件  CPU: 内存: 显示器:1920*1080 60Hz 硬盘空间: 40GB   

作者:刘传玺 、盛洁、黄子轩

一. 实验目的及实验环境 

1.实验目的

通过编译和链接一个程序,深入理编译和链接都做了什么,并掌握静态库和动态链接库的编写和调用方法。

2.实验环境     

(1)硬件 

CPU:


内存:


显示器:1920*1080 60Hz

硬盘空间: 40GB   

(2)软件  

虚拟机名称及版本:VMware

操作系统名称及版本:Ubuntu16.04

编译器:gcc


二. 实验内容 

1、实验前准备工作  

1)阅读参考资料,了解编译链接的过程

C/C++语言编写的程序转换成为处理器能够执行的二进制代码的过程,包括四个 步骤: 

预处理(Preprocessing)

编译(Compilation)

汇编(Assembly)

链接(Linking) 

其中编译就是把预处理之后的文件进行一系列词法分析、语法分析、语义分析以及优化后生成的相应汇编代码文件。汇编就是将编译后的汇编代码翻译为机器码,几乎每一条汇编指令对应一句机器码。链接是将汇编产生的目标文件和所使用的库函数的目标文件链接生成一个可执行文件的过程。


2)学习gcc、size、ar、ldd、readelf、nm等命令的使用

gcc 命令


size 命令 

size 命令基本上就是输出指定输入文件各段及其总和的大小。 

命令

size  目标文件/可执行文件名 输出文本段、数据段和 bss 段及其相应的大小。然后是十进制格式和十六进制格式的总大小。最后是文件名。
size 目标文件/可执行文件名 --format=SysV 切换输出格式
size 目标文件/可执行文件名 -d 各个段的大小以十进制数字的格式显示
size 目标文件/可执行文件名 -o 各个段的大小以八进制数字的格式显示
size 目标文件/可执行文件名 -x
各个段的大小以十六进制数字的格式显示
size -t [file1] [file2] ... 如果用 size 一次性查找多个文件的段大小,则通过使用 -t 选项还可 以让它显示各列值的总和。
size --common [file1] [file2] ... 输出每个文件中公共符号的总大小


ar 命令

用于建立或修改备存文件,或是从备存文件中抽取文件。

语法:ar[-dmpqrtx][cfosSuvV][a<成员文件>][备存文件][成员文件] 

下表是常见命令选项 


ldd 命令

可以查看一个可执行程序依赖的共享库 

下表是常见命令选项


readelf 命令

用于显示读取 ELF 文件中信息 

格式:readelf <option(s)> elf-file(s) 

下表是常见命令选项


nm 命令

可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。

对目标文件和可执行文件而言, 可以获得其中的函数;

另外可以从静态库和动态库中获取到函数名称。


3)把原来写好的生产者-消费者问题的代码准备好。


2、实验要求

从生产者-消费者,读者-写者,哲学家就餐问题的中选择一个自己感兴趣的 代码对其进行改造,将其拆分成 2 个以上的文件单独编译。 
1)通过 ar 命令将其打包成静态库,并调用自己的静态库编写程序运行, 查看结果,并用 size 命令查看各个段的大小。 

2)通过 gcc 产生动态链接库,并运行,用 ldd 命令查看文件的依赖。 


3、提问并回答 

在讨论区提出至少两个问题,并给予回答,或同组内,两个同学为一组,一 个提问,一个回答。 


三.方案设计 

1.给出静态库生成的过程方案。

我使用的是生产者消费者的代码,我将宏定义和函数放到 lcx.h 这个头文件中,再将函数定义和全局变量放到 lcx.c 这个文件中,主函数放到 main.c 这个文件中作为程序入口。之后,步骤如下: 

第一步:编辑源文件,lcx.h,lcx.c,main.c。其中 main.c 文件中包含 main 函 数,作为程序入口;main.c 中包含 main 函数中需要用到的函数。

第二步:将 main.c 编译成目标文件。gcc -c lcx.c,得到 lcx.o 这个目标文件。

第三步:由.o 文件创建静态库。ar -rcs lcx.a lcx.o 创建完成后可以使用nm 查看 lcx.a 中的内容。使用 ar -t lcx.a 也可以 

第四步:在程序中使用静态库。gcc main.c -L. -l lcx.a -lpthread -L 指出链接的库在当前目录下,-l 加链接库的名字 因为是静态编译,生成的执行文件可以独立于.a 文件运行。

第五步:执行。

2. 给出动态库生成的过程

第一步:编辑源文件,与创建静态库相同,代码无变化

第二步:由 lcx.c 文件创建动态库文件。

gcc  -fPIC  -shared -o lcx.so lcx.c
这里一定要用-o 重命名选项,不然默认输出文件为 a.out 与编译出的可执行文件重名,到时候编译出来的可执行文件会覆盖掉动态库。

这里也可以使用 nm lcx.so 来查看动态库中的内容

第三步:在程序中使用动态库。gcc  main.c -L  -l ./lcx.so -lpthread

这里动态库的路径最好使用绝对路径或相对路径,如果只写文件名容易报 错。

第四步:执行。


四.总结

1.实验过程中遇到的问题及解决办法

问题:所遇到的问题请见 error 截图,是编译过程的错误。

解决办法:利用 vimf1.c 进行代码的查验,发现没有错误,于是考虑到可能是由于子代码的错误导致进行静态库创建出现了问题,于是对 test.c 进行查验,发现 void* producer 函数头没有进行编写,而是将其内容作为 print()函数的功能,另外补充了 print()函数后成功编译。
2.对设计及调试过程的心得体会

心得体会:在这次编译代码利用静态库和动态库的过程中我学到了很多知识,静态库或静态链接库是一组例程,外部函数和变量,它们在编译时在调用者中解析,并由编译器、链接器或绑定器复制到目标应用程序中,从而生成目标文件和一个独立的可 执行文件。动态链接只包括库的地址(而静态链接是浪费空间)动态链接在运行时链接库。 

静态库虽然可以在多个程序中重用,但在编译时会被锁定到程序中。另一方面, 动态或共享库作为可执行文件之外的单独文件存在。接下来我分析动态库静态库的优缺点,使用静态库的缺点是它的代码被锁定到最终的可执行文件中,如果没有重新编译就无法修改。相反,可以修改动态库而无 需重新编译。 

由于动态库位于可执行文件之外,因此程序只需在编译时制作库文件的一个 副本。而使用静态库意味着程序中的每个文件都必须在编译时拥有它自己的库文件副本。 

使用动态库的缺点是程序更容易破坏。例如,如果动态库损坏,则可执行文 件可能不再起作用。但是,静态库是不可触及的,因为它存在于可执行文件中。 

使用动态库的好处是,多个正在运行的应用程序可以使用相同的库,而无需 每个应用程序拥有自己的副本。 

最后分析就可以得出它们的适用范围,如果你有很多文件,静态库的多个副 本意味着可执行文件的大小增加,那就建议使用动态库,可以节省时间。如果执行时间的好处超过节省空间的需要,那么静态库就是最佳选择。

五.附录:源代码

main.c 

//pv操作:生产者与消费者经典问题 //author:leaf #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h>#include "lcx.h" extern int in; /*生产者放置产品的位置*/ extern int out;        /*消费者取产品的位置*/ extern int buff[M]; /*缓冲初始化为0, 开始时没有产品*/ extern sem_t sem_dr; /*同步信号量,当满了时阻止生产者放产品*/ extern sem_t sem_co; /*同步信号量,当没产品时阻止消费者消费*/ extern pthread_mutex_t mutex; /*互斥信号量, 一次只有一个线程访问缓冲*/ int main(){   pthread_t id1;  pthread_t id2;  pthread_t id3;  pthread_t id4;  int i;   int ret;   sem_mutex_init();   /*create the producer thread*/    ret = pthread_create(&id1, NULL , producer, NULL );    if (ret != 0)    {      printf ("producer creation failed \n");      exit (1);    }    ret = pthread_create(&id3, NULL , producer, NULL );    if (ret != 0)    {      printf ("producer creation failed \n");      exit (1);    }    /*create the consumer thread*/    ret = pthread_create(&id2, NULL , consumer, NULL );    if (ret != 0)    {      printf ("consumer creation failed \n");      exit (1);    }    ret = pthread_create(&id4, NULL , consumer, NULL );    if (ret != 0)    {      printf ("consumer creation failed \n");      exit (1);    }   pthread_join(id1, NULL );  pthread_join(id2, NULL );   pthread_join(id3, NULL );    pthread_join(id4, NULL );   exit (0); 

lcx.h 

#ifndef LCX_H #define LCX_h #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #define M 32 /*缓冲数目*/ #define P( x )    sem_wait(& x ) #define V( x ) sem_post(& x ) void print();void* producer(); void* consumer(); void sem_mutex_init(); #endif

lcx.c 

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #include "lcx.h" int in = 0; /*生产者放置产品的位置*/ int out = 0;         /*消费者取产品的位置*/ int buff[M] = { 0 };   /*缓冲初始化为0, 开始时没有产品*/ sem_t sem_dr;          /*同步信号量,当满了时阻止生产者放产品*/ sem_t sem_co; /*同步信号量,当没产品时阻止消费者消费*/ pthread_mutex_t mutex; /*互斥信号量, 一次只有一个线程访问缓冲*/ /*  *output the buffer  */ void print() {  int i;  for (i = 0; i < M; i++)  printf ("%d ", buff[i]);   printf ("\n"); /*  *producer  */ void* producer() {  for (;;)  {     sleep(1);     P(sem_dr);             pthread_mutex_lock(&mutex);     in = in % M;             printf ("(+)produce a product. buffer:");     buff[in] = 1;             print();             ++in;      pthread_mutex_unlock(&mutex);             V (sem_co);       } /*  *consumer  */ void* consumer(){       for (;;)   {             sleep(2);      P(sem_co);             pthread_mutex_lock(&mutex);     out = out % M;             printf ("(-)consume a product. buffer:");     buff[out] = 0;             print();             ++out;     pthread_mutex_unlock(&mutex);             V (sem_dr);       } void sem_mutex_init() { /*  *semaphore initialize  */  int init1 = sem_init(&sem_dr, 0, M);  int init2 = sem_init(&sem_co, 0, 0);  if ((init1 != 0) && (init2 != 0))  {  printf ("sem init failed \n");  exit (1);    }     /*       *mutex initialize       */      int init3 = pthread_mutex_init(&mutex, NULL );      if (init3 != 0)      {          printf ("mutex init failed \n");          exit (1);       } }  



猜你喜欢:

【Linux笔记】设备树实例分析

【Linux笔记】通俗易懂的Linux驱动基础

【Linux笔记】pc机_开发板_ubuntu互ping实验
【Linux笔记】挂载网络文件系统

学习STM32的一些经验分享

基于LiteOS的智慧农业案例实验分享

从单片机工程师的角度看嵌入式Linux

笔记:编写简单的内核模块


后台回复:加群。添加ZhengN微信,加入交流群



点个赞,证明你还爱我


免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭