嵌入式Linux应用开发基础-现有动态库so的使用

前言

最近做嵌入式Linux项目,需要调用客户提供的现成的动态库(so文件,包含对应头文件),我这边用的是cmake来构建。
此篇文章主要是记录一下嵌入式Linux的动态库的使用,与君共勉!

一、通过cmake使用so库和对应的头文件
1.包含so库对应的头文件目录:
(1)当你调用了so库对应的头文件,为了编译通过,需要把so库对应的头文件的目录包含进去
(2)cmake通过include_directories指令包含目录,这个指令必须在生成可执行文件add_executable指令之前
(3)${PROJECT_SOURCE_DIR}是相对地址前缀,代表此CMakeLists.txt所在的目录
(4)目录2和目录4为需要包含的头文件的目录

#包含目录的例子
include_directories(
	${PROJECT_SOURCE_DIR}/目录1/目录2
	${PROJECT_SOURCE_DIR}/目录3/目录4
)

2.链接so库文件:
(1)动态库只有在调用它的进程启动的时候才会被加载,因此在编译的时候,这个so库甚至不需要在工程里,只要对应的头文件目录包含了,就能编译通过,生成可执行文件
(2)生成可执行文件后,要把可执行文件用到的动态库进行链接,可执行文件运行的时候才知道要加载哪些动态库(能不能找到库另说)
(3)camke通过target_link_libraries指令链接库,这个指令必须在生成可执行文件add_executable指令之后
(link_libraries指令也是链接库,但是必须在生成可执行文件add_executable指令之前。因为静态库需要编译时加载,因此静态库一般都用这个指令,这也是使用静态库的可执行文件比使用动态库的可执行文件更大的原因)
(4)AAA是生成的可执行文件名;BBB是动态库名字的简写:动态库的名字必须是libXXX.so,填名字时省略"lib"和".so",只填XXX就行

#可执行文件名:AAA,动态库名字:libBBB.so
#链接动态库
target_link_libraries(AAA BBB)

二、通过cmake安装so库到target目录
通过以上步骤,可执行文件已经编译完成,并且可执行文件运行的时候需要加载哪些动态库了。接下来我们要让可执行文件能正确地搜索到需要的库!

1.安装so库到target目录:
(1)可执行文件运行的时候,是在target目录里搜索库的
(2)嵌入式Linux 系统把 target目录里的/lib 和 /usr/lib 两个目录作为默认的库搜索路径
(3)camke通过install指令进行安装。它可以用来安装很多内容,可以包括目标二进制、动态库、静态库以及文件、目录、脚本等。其中,so库可以通过目标二进制(TARGETS)和普通文件(FILES)来进行安装

install(TARGETS <target>... [...])
install({FILES | PROGRAMS} <file>... [...])
install(DIRECTORY <dir>... [...])
install(SCRIPT <file> [...])
install(CODE <code> [...])
install(EXPORT <export-name> [...])

(4)如果是别人提供的so库,按照普通文件(FILES)来安装,其中${PROJECT_SOURCE_DIR}是相对地址前缀,代表此CMakeLists.txt所在的目录;DESTINATION是target文件夹

#动态库名字:libAAA.so
#目标安装位置:target/usr/lib
#在target安装动态库
install(FILES ${PROJECT_SOURCE_DIR}/目录1/.../目录N/libAAA.so DESTINATION /usr/lib)

(5)如果是自己生产的so库,按照目标二进制(TARGETS)来安装,先通过add_library生成动态库,再把动态库名字作为TARGETS名进行安装

#源文件名字:AAA.c,生成的动态库名字:libAAA.so
#生成动态库
add_library(AAA SHARED ${PROJECT_SOURCE_DIR}/AAA.c)

#目标安装位置:target/usr/lib
#在target安装动态库
INSTALL(TARGETS AAA
       LIBRARY DESTINATION /usr/lib
)

三、代码里调用so库(显示调用/隐式调用)
1.隐式调用
(1)隐式调用由系统完成,对程序员是不可感知的
(2)写代码时使用方法和静态库一样,包含头文件,直接使用数据结构和接口函数即可

2.显式调用
(1)显式调用的so库,如果只需要用到接口函数,不需要数据结构的话,连头文件的都不需要包含就能用
(2)显式调用则要求程序员在调用时,指明要加载的动态库的名称和要调用的函数名称
(3)显式调用是在调用so库时才申请空间,使用结束可释放,会相对更省空间
(4)函数介绍:

dlopen
函数原型:void *dlopen(const char *libname,int flag);
功能描述:dlopen必须在dlerror,dlsym和dlclose之前调用,表示要将库装载到内存,准备使用。如果要装载的库依赖于其它库,必须首先装载依赖库。如果dlopen操作失败,返回NULL值;如果库已经被装载过,则dlopen会返回同样的句柄。

参数中的libname一般是库的全路径,这样dlopen会直接装载该文件;如果只是指定了库名称,在dlopen会按照下面的机制去搜寻:

a.根据环境变量LD_LIBRARY_PATH查找

b.根据/etc/ld.so.cache查找

c.查找依次在/lib和/usr/lib目录查找。

flag参数表示处理未定义函数的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;RTLD_NOW表示马上检查是否存在未定义的函数,若存在,则dlopen以失败告终。

dlerror
函数原型:char *dlerror(void);
功能描述:dlerror可以获得最近一次dlopen,dlsym或dlclose操作的错误信息,返回NULL表示无错误。dlerror在返回错误信息的同时,也会清除错误信息。

dlsym
函数原型:void *dlsym(void *handle,const char *symbol);
功能描述:在dlopen之后,库被装载到内存。dlsym可以获得指定函数(symbol)在内存中的位置(指针)。如果找不到指定函数,则dlsym会返回NULL值。但判断函数是否存在最好的方法是使用dlerror函数,

dlclose
函数原型:int dlclose(void *);
功能描述:将已经装载的库句柄减一,如果句柄减至零,则该库会被卸载。如果存在析构函数,则在dlclose之后,析构函数会被调用。

//显式调用例程
typedef struct
{
	void *pd;
	int (*Init_func)(void* param);
	int (*getData_func)(unsigned char* buff, int bufflen);	
}func_t;

func_t fun;
//打开动态链接库,其中./usr/lib/libAAA.so"为库在target的位置
fun.pd=dlopen("./usr/lib/libAAA.so",RTLD_LAZY);
if (fun.pd==NULL) /* 若打开失败则退出 */ 
{
	perror(dlerror()); 
	perror(stderr);
	return -2;
}

//动态链接接口函数,其中AAA_Init为库里的接口函数
fun.Init_func=dlsym(fun.pd,"AAA_Init");
error=dlerror();
if (error) /* 若出错则退出 */ 
{ 
	//perror(error,stderr); 
	perror(error);
	perror(stderr);
	return -3; 
}

//动态链接接口函数,其中AAA_Get_ModuleData为库里的接口函数
fun.getData_func=dlsym(fun.pd,"AAA_Get_ModuleData");  /* 动态链接接口函数 */ 
error=dlerror();
if (error) /* 若出错则退出 */ 
{ 
	//perror(error,stderr); 
	perror(error);
	perror(stderr);
	return -3; 
}

//调用链接的接口
uint8_t buf[2]={0};
if (fun.Init_func(buf) < 0)
{
	perror("Init_funcfailed!\n");
	return -4;
}

//调用链接的接口
uint8_t dataBuf[256]={0};
if(fun.getData_func(dataBuf,sizeof(dataBuf))>0)
{
	...
}

//关闭链接,释放内存
dlclose(fun.pd);
  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 嵌入Linux应用开发第五章示例3是一个关于文件操作的代码示例。该示例主要涉及到如何在Linux嵌入系统中使用文件操作的相关函数。 首先,在示例代码中引入了几个与文件操作相关的头文件,包括<fcntl.h>、<sys/types.h>和<sys/stat.h>。这些头文件中定义了一些文件打开、读写和关闭等函数以及文件的相关数据类型。 接下来,代码示例中定义了一个用于存储文件路径的字符串变量path和一个用于存储文件描述符的整型变量fd。在示例中,path被指定为了一个具体的文件路径,这个路径可以根据实际情况进行修改。fd用于存储打开的文件的描述符。使用函数open()来打开文件,并将返回的文件描述符保存到变量fd中。在代码示例中,函数open()的第一个参数为文件路径,第二个参数为打开文件的方,第三个参数为文件的权限设置。 接下来,代码示例使用函数read()来读取打开的文件中的内容,并将读取到的内容存储到一个用于存储读取结果的字符数组buf中。函数read()的第一个参数为文件描述符,第二个参数为存储读取结果的缓冲区地址,第三个参数为缓冲区的大小。 最后,在代码示例的结尾部分,使用函数close()来关闭文件,传入文件描述符作为参数。 总结而言,该示例代码主要演示了在嵌入Linux系统中如何进行文件操作,包括打开文件、读取文件内容和关闭文件等操作。通过学习该示例,可以了解到Linux系统中与文件操作相关的函数和头文件的使用方法。 ### 回答2: 嵌入Linux应用开发第五章示例3代码主要涉及嵌入设备中使用动态开发过程。该示例中使用了一个简单的动态,主要实现了一个计算两个整数和的函数。 首先,在示例代码中,我们首先需要编写一个动态的C源文件,可以为该文件命名为libadd.c。该源文件中定义了一个函数add,用于计算两个整数的和。然后,我们使用gcc编译器将该源文件编译为一个动态使用的命令是:gcc -shared -o libadd.so libadd.c。编译后的动态文件为libadd.so。 接下来,在示例代码中,我们需要编写一个可执行文件的C源文件,可以为该文件命名为main.c。在该源文件中,我们通过使用dlopen函数动态加载libadd.so动态,并通过dlsym函数获取动态中的add函数的地址。然后,我们可以调用add函数来计算两个整数的和,并输出结果。最后,我们使用gcc编译器将该源文件与动态链接为一个可执行文件,使用的命令是:gcc -o main main.c -ldl。编译后的可执行文件为main。 最后,在示例代码中,第三个函数是main函数,其中通过调用add函数来计算两个整数的和,并输出结果。 通过以上步骤,我们成功实现了嵌入设备中动态使用使用动态可以提供代码的复用性,减少可执行文件的大小,并且方便进行代码的更新和维护。在实际应用中,我们可以根据需要编写更多的动态,并将其集成到嵌入系统中,以实现各种功能和服务。 ### 回答3: 第五章示例3代码是一个嵌入Linux应用开发的示例程序。这个示例程序主要展示了如何在Linux系统上使用C语言编写一个简单的应用程序,以及如何利用Linux内核提供的接口对系统进行控制和管理。 代码的主要结构是一个无限循环,循环中不断读取用户输入的按键值,然后根据按键值执行相应的操作。代码中使用Linux系统提供的头文件和函数来实现输入和输出操作。 首先,在程序开始的部分,设置了一些初始化的参数。包括定义了按键值的变量和输入输出设备的文件描述符。 接着,在无限循环中,使用read函数读取用户输入的按键值,并将其存储在按键变量中。然后,通过switch语句对按键进行判断和处理。 代码中的示例操作比较简单,主要是根据不同的按键值打印相应的提示信息。例如,按下“1”键会打印“Hello World”等等。 最后,在代码的结尾处,关闭了打开的输入输出设备的文件描述符。 总的来说,这个示例程序展示了如何在嵌入Linux系统中进行应用开发和控制,以及如何利用Linux内核提供的接口进行输入和输出操作。这个示例程序比较简单,但是可以作为学习和理解嵌入Linux应用开发基础
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值