【Linux】第六篇:静动态库文件的理解及实现


在这里插入图片描述


基本概念

  • 静态库(.a):程序在编译链接的时候把库的代码直接装载到可执行文件中。程序运行的时候将不再需要第三方库。打个比方:静态库如同私家车,停放的位置随意,用户可直接使用,缺点在于去哪儿都得带着这辆车,本体移动显得笨重。
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码,依赖第三方库。打个比方:动态库好比公交车,你只能去到固定的地方(公交车站台)才能使用(调用),而且可以多人共享,到达目的地后便可下车,不用去哪都带着这辆公交车,可做到本体轻量化,缺点在于一旦没有了这辆公交车,或者找不到公交站台,你什么事都办不了。

由于静态库会提前加载到进程中的代码段,如若有多个C语言程序,其c静态库的代码是冗余的,而动态库在进程调用时会加载到内存中,然后与虚拟地址空间的页表产生映射即可,所以动态库仅需一份,然后映射至多个进程即可,代码不会造成冗余。

  • ls /usr/include 查看当前系统的库文件

  • man 库文件

    查看库文件的手册,如:man string.h

  • ldd 文件名 显示当前可执行文件依赖的动态库

    我们对该库详加查看:

    发现是一个软链接,库实际上是文件,也拥有inode。

库的命名

动态库 libXXX.so:XXX为库名

动态库 libXXX.a:XXX为库名

所以 libc.so.6 为c库,libc++.so.6 为c++库。

安装c静态库指令

服务器可能没有静态库,这里提供c语言静态库的安装指令:

sudo yum install glibs-static

  • 默认gcc编译链接的是动态库,若要链接为静态,需手动添加 -static

    由于是链接静态库,ldd指令无法查看。

  • 可通过 file查看库的链接情况

静态库的打包与使用

静态库打包

我们先写两个计算加减法的函数,将其打包成静态库

add.h

#ifndef _ADD_H_
#define _ADD_H_

int add(int a,int b);

#endif

add.c


#include "add.h"

int add(int a,int b)
{
    return a+b;
}

sub.h

#ifndef _SUB_H_
#define _SUB_H_

int sub(int a,int b);

#endif

sub.c

#include "sub.h"

int sub(int a,int b)
{
    return a-b;
}

现在要对上面的函数接口打包成静态库:

  1. 首先将.c和.h进行编译获得.o文件 -c
gcc -c add.c -o add.o
gcc -c sub.c -o sub.o

  1. 生成静态库
  • ar -rc 静态库名 add.o sub.o

    ar : gnu的归档工具,用于建立或修改备存文件,或是从备存文件中抽取文件。可集合许多文件,成为单一的备存文件。在备存文件中,所有成员文件皆保有原来的属性与权限。

    • -d 删除备存文件中的成员文件
    • -m 变更成员文件在备存文件中的次序
    • -p 显示备存文件中的成员文件内容
    • -q 将文件附加在备存文件末端
    • -r 将文件插入备存文件中
    • -x 自备存文件中取出成员文件

    -rc : replace and creat,表示当生成静态库的.o文件发生改变时会更新静态库

  • ar -tv 静态库名 查看静态库的目录列表

    • t:列出静态库中的文件
    • v:verbose 详细信息

调用静态库

我们将上面的静态库整理一下,.h文件放在include文件夹中,静态库放在lib文件夹中,使其看上去像个正经的静态库:

我们此时对包含了add.h和sub.h的main.c程序进行编译

程序没有找到这个静态库,因为我们自定义的静态库路径并不在程序的环境变量中,而且也不在当前目录中,所以我们要在gcc编译时,引入我们自定义的静态库的路径

  • -l (小写L)指定库名,拿数学库来说,他的库名是m,他的库文件名是libm.so。

    这里我们指定的库为libmymath.a 所以库名指定-lmymath

  • -L (L for Library)指定自定义库的搜索路径(由于自定义库的路径下可能包含多个库,所以一定要指定库名)

    这里我们要连接的静态库文件的路径就是 -L./mylib/lib

  • -I (大写i for include)指定自定义头文件路径

    这里我们要连接的头文件的路径就是 -I./mylib/include

总结:生成静态可执行程序(gcc -I+头文件路径-L+库路径-l+要链接的库名)

修改Makefile后重新编译,即可通过。

以上的路径为相对路径也可以是绝对路径。

我们可以在Makefile文件中,通过shell指令添加绝对路径

  • path=$(shell pwd)

Makefile编写如下:

path=$(shell pwd)
main-static.exe:main.c
    #-I 指定头文件路径 -L 库目录路径 -l 指定库目录名称
	gcc -o $@ $^ -I $(path)/mylib/include -L $(path)/mylib/lib -lmymath -static
.PHONY:clean
clean:
		rm -rf main.exe

我们当然可以把头文件和库文件分别置于系统默认的头文件和库文件文件夹中:

头文件目录:/usr/include/
库文件目录:/usr/lib64

但不建议这么做,会污染命名池。

动态库的打包与使用

动态库打包

延用之前的函数库,我们将他们打包成动态库

Makefile 文件编写如下

libmymath.so:add.o sub.o
		gcc -shared -o $@ $^

add.o:add.c
		gcc -fPIC -c $^ 

sub.o:sub.c
		gcc -fPIC -c $^

.PHONY:clean
clean:
		rm -rf libmymath.so *.o mylibso_output
		

#发布,将头文件和库文件做一个打包
.PHONY:output
output:
		mkdir -p mylibso_output/include
		mkdir -p mylibso_output/lib 
		cp *.h mylibso_output/include
		cp *.so mylibso_output/lib

  • -shared: 表示生成共享库格式
  • -fPIC:产生位置无关码(position independent code)
  • 库名规则:libxxx.so
  • 将头文件和库文件打包方便交付

make+make output 后得到了发布的动态库

动态库调用

将动态库复制到main.c所在文件夹下

//main.c
#include <stdio.h>
#include "add.h"
#include "sub.h"
int main()
{
    printf("%d\n",add(10,20)); 
    printf("%d\n",sub(10,20)); 
    printf("hello world\n");

    return 0;
}

我们试着延用调用静态库时的Makefile文件,gcc去掉 -static选项,并重新填入动态库的头文件,库文件以及库名。

path=$(shell pwd)
main-dynamic.exe:main.c
	#-I 头文件路径 -L 库目录文件路径 -l 指定库名
	gcc -o $@ $^ -I $(path)/mylibso_output/include \
                 -L $(path)/mylibso_output/lib \
                 -lmymath 
.PHONY:clean
clean:
		rm -rf main-dynamic.exe

可成功通过编译

但是没有办法运行

原因在于:我们只告诉了编译器动态库的位置,但是当程序运行时,操作系统并不知道链接的动态库的位置,故无法调用。

所以我们可以把自己写的动态库文件添加进系统默认的动态库搜索路径:

  • LD_LIBRARY_PATH 环境变量 动态库的路径

链接指令如下

现在使用ldd查看我们的可执行文件,发现已经连接上了动态库

—end—

青山不改 绿水长流

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
课程设计报告书 课程名称: 操作系统原理 题 目: 基于Android系统的音乐播放器设计与实现 2012 年 7 月 5 日 课程设计任务书 设计题目:基于Android 系统的音乐播放器设计与实现 初始条件: Linux操作系统,Android开发环境,Java编译环境,SQLite数据 要求完成的主要任务: 主要任务: 现今社会生活紧张,而欣赏音乐是其中最好的舒缓压力的方式之一,本项目的目的是 开发一个可以播放主流音乐文件格式的播放器的播放功能模块,要求音乐播放器能够播 放Mp3,Wav多种格式的音乐文件,能够控制播放,暂停,停止,上一曲,下一曲,能够 调节音量,能播放RMVB格式的视频文件,此外,还能支持中文、英文等语言界面。要求 视觉外观美观,操作简单。 本项目是一款基于Android手机平台的音乐播放器的子模块,使Android手机拥有个性 的多媒体播放器,使手机显得更生动灵活化,与人们更为接近,让手机主人随时随地处 于音乐视频的旋律之中。使人们的生活更加多样化。也使设计者更加熟练Android的技术 和其它在市场上的特点 设计报告撰写格式要求: 1设计题目与要求 2 设计思想 3系统结构 4 数据结构的说明和模块的算法流程图 5 使用说明书(即用户手册):内容包含如何登录、退出、读、写等操作说明 6 运行结果和结果分析(其中包括实验的检查结果、程序的运行情况) 7 自我评价与总结 8 附录:程序清单,注意加注释(包括关键字、方法、变量等),在每个模块前加注释; 时间安排 7月1日 布置课程设计任务;分配题目后,查阅资料、 准备程序; 7月 2~7月4 日上机调试程序、书写课程设计报告; 7月5 日 提交课程设计报告及相关文档。 基于Android平台的音乐播放器的播放控制功能设计 摘要Android简介: Android是一种以Linux为基础的开放源码操作系统,主要使用于便携设备。目前尚未 有统一中文名称,中国大陆地区较多人使用"安卓"或"安致"。Android操作系统最初由A ndy Rubin开发,最初主要支持手机。2005年由Google收购注资,并组建开放手机联盟开发改 良,逐渐扩展到平板电脑及其他领域上。 Android平台五大优势特色: 1、开放性 2、挣脱运营商的束缚 3、丰富的硬件选择 4、不受任何限制的开发商 5、无缝结合的Google应用  图 Android 软件体系结构图 Android作为一个移动设备的平台,其软件层次结构包括了一个操作系统(OS),中间件 (MiddleWare)和应用程序(Application)。根据Android的软件框图,其软件层次结 构自下而上分为以下几个层次: 第一、操作系统层(OS) 第二、各种(Libraries)和Android 运行环境(RunTime) 第三、应用程序框架(Application Framework) 第四、应用程序(Application) 1、设计题目与要求 题目:基于Android平台的音乐播放器的播放控制功能设计 要求:现今社会生活紧张,而欣赏音乐是其中最好的舒缓压力的方式之一,本项目的 目的是开发一个可以播放主流音乐文件格式的播放器的播放功能模块,要求音乐播放器 能够播放Mp3,Wav多种格式的音乐文件,能够控制播放,暂停,停止,上一曲,下一曲 ,能够调节音量,能播放RMVB格式的视频文件,此外,还能支持中文、英文等语言界面 。要求视觉外观美观,操作简单。 本项目是一款基于Android手机平台的音乐播放器的子模块,使Android手机拥有个性 的多媒体播放器,使手机显得更生动灵活化,与人们更为接近,让手机主人随时随地处 于音乐视频的旋律之中。使人们的生活更加多样化。也使设计者更加熟练Android的技术 和其它在市场上的特点。 2、设计思想 要实现这个音乐播放器。首先应该考虑到的就是他的功能,要有后台播放功能以及显 示正在播放的文件和把手机中的音乐文件以列表的形式显示在手机上,这就要使用到li stview类。 另外在本地歌曲列表和正在播放的歌曲之间要进行动态菜单的跳转故而想到用tabho st这个容器进行布局。后台播放创建一个类继承service方法,在该类中使其具有播放暂 停下一首上一首的功能的方法,从而是它在各个Activity之间进行跳转的时候为其他的 类提供相应的方法。创建一个新建文件类和过滤文件信息的类,用以存贮特定的文件信 息和当用户改变文件夹终不改的内容的时候同步更新播放列表。于接打电话这个问题, 创建一个类进行监听,当电话进来或出去的时候调用service方法停止音乐播放并记下播 放点。 对于刚接触Android开发的我们,在很多的知识点上还是不是很理解,鉴于此在制作 的过程难免会遇到各种问题,解
很好,我已经理解了你的问题。以下是答案: 1. imx6ull的主频是 528MHz,这个芯片是 NXP 公司的产品,该产品是多核的,包括一个 Cortex-A7 核和一个 Cortex-M4 核。它是基于 ARM 架构的产品。 2. GPIO的初始化过程一般包括以下步骤:选择 GPIO 复用功能、配置输入输出方向、配置输入输出电平、配置中断方式。 3. 在 Linux 下编译动态库需要用到 -shared 选项,编译静态库需要用到 -static 选项。具体命令可以参考以下示例:编译动态库:gcc -shared -o libtest.so test.c;编译静态库:gcc -c test.c && ar rcs libtest.a test.o。 4. SPI总线的初始化过程一般包括以下步骤:选择 SPI 模式、选择数据传输方式、选择时钟极性和相位、选择数据位宽、选择时钟频率、使能 SPI 总线。 5. LINUX内核的引导过程分成 4 个阶段,分别是:BIOS/UEFI 阶段、BootLoader 阶段、内核引导阶段、用户空间启动阶段。 6. I2C发送的第一帧是 START 信号。 7. UART的起始状态是 IDLE 状态。 8. 对于 SPI 的外部设备,需要进行以下初始化步骤:选择 SPI 模式、选择数据传输方式、选择时钟极性和相位、选择数据位宽、选择时钟频率、使能 SPI 总线,然后根据外设的需要进行相应的初始化。 9. 递归查找 .C 文件内容里还有“main”单词的查询命令可以使用以下命令:grep -r "main" *.c。 10. git 一般情况下的提交流程如下:在本地修改文件 → 将文件添加到暂存区 → 提交到本地仓 → 推送到远程仓。具体命令包括:git add [file]、git commit -m "message"、git push [remote] [branch]。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值