最新电子科大李老师Linux实验四.docx
《最新电子科大李老师Linux实验四.docx》由会员分享,可在线阅读,更多相关《最新电子科大李老师Linux实验四.docx(18页珍藏版)》请在冰豆网上搜索。
![最新电子科大李老师Linux实验四.docx](https://file1.bdocx.com/fileroot1/2022-12/15/0e87ca6e-e79b-4d52-a8e3-fa1c8571fe92/0e87ca6e-e79b-4d52-a8e3-fa1c8571fe921.gif)
最新电子科大李老师Linux实验四
电子科大李老师Linux实验四
电子科技大学
实验报告
学生姓名:
学号:
指导教师:
李林
实验地点:
实验时间:
Csdn全部资源下载地址:
一、实验室名称:
Linux环境高级编程实验室
二、实验项目名称:
插件框架实验
三、实验学时:
4学时
四、实验目的:
需要说明为什么要进行本次实验
五、实验内容:
⏹版本1:
开发一个程序,向屏幕打印“HelloWorld”;
在不重新编译链接原程序的前提下,将打印的文字改为“HelloChina”
思想:
将打印函数编译为一个动态链接库文件,然后测试程序用显式调用的方式,加载动态链接库,并调用其中的打印函数。
此后要改变输出内容,只需要重新编译动态链接库文件,而无需对测试程序进行修改。
动态链接库源文件代码(导出打印函数),print.cpp:
#include
usingnamespacestd;
//打印函数定义
extern"C"voidPrint()
{
cout<<"HelloWorld!
"<}
测试程序源代码,test.cpp:
#include
#include
usingnamespacestd;
//主程序,测试动态链接库
intmain()
{
//打开动态链接库文件
void*handle=dlopen("./libprint.so",RTLD_LAZY);
if(handle==0)//打开失败
{
cout<<"dlopenerror"<return0;
}
//定义一个函数指针类型
typedefvoid(*FUNC_PRINT)();
//映射打印函数地址
FUNC_PRINTdl_print=(FUNC_PRINT)dlsym(handle,"Print");
if(dl_print==0)
{
cout<<"dlsymerror"<return0;
}
//调用打印函数
(dl_print)();
//卸载动态链接库
dlclose(handle);
return0;
}
先编译测试程序
再编译动态链接库
运行测试程序,输出HelloWorld
修改print.cpp里的输出内容为HelloChina
重新编译动态链接库文件之后,再运行测试程序,输出了HelloChina
⏹版本2要求:
同时要打印“HelloWorld”,打印“HelloChina”,甚至同时打印未来才会增加的其他打印信息
打印未来的这些信息,也不能重新编译链接原程序
思想:
使用插件的思想,将所有的动态链接库文件放置在一个plugin的文件夹中,然后测试程序去遍历该目录,依次打开所有的动态链接库文件,并调用其中的Print函数。
另外遍历目录获取动态链接库文件名这个任务,定义一个单独的工具类来实现。
第一个动态链接库源代码,print_world.cpp:
#include
usingnamespacestd;
//打印函数定义
extern"C"voidPrint()
{
cout<<"HelloWorld!
"<}
第二个动态链接库源代码,print_china.cpp:
#include
usingnamespacestd;
//打印函数定义
extern"C"voidPrint()
{
cout<<"HelloChina!
"<}
插件文件搜索类头文件,CPluginSeacher.h:
#ifndefCPLUGINSEARCHER_H
#defineCPLUGINSEARCHER_H
#include
#include
usingnamespacestd;
//插件搜索类声明
classCPluginSeacher
{
public:
CPluginSeacher();
virtual~CPluginSeacher();
//获取所有的插件文件名
boolGetPluginNames(vector&vstrPluginNames);
};
#endif
插件文件搜索类源文件,CPluginSeacher.cpp:
#include"CPluginSeacher.h"
#include
#include
CPluginSeacher:
:
CPluginSeacher()
{
}
CPluginSeacher:
:
~CPluginSeacher()
{
}
//获取所有的插件文件名
boolCPluginSeacher:
:
GetPluginNames(vector&vstrPluginNames)
{
//打开当前目录下的plugin目录
DIR*dir=opendir("./plugin");
if(dir==0)
returnfalse;
//循环读取所有文件
for(;;)
{
//读一个文件
structdirent*pentry=readdir(dir);
if(pentry==0)
break;
//当前目录,跳过
if(strcmp(pentry->d_name,".")==0)
continue;
//上级目录,跳过
if(strcmp(pentry->d_name,"..")==0)
continue;
//其它文件,则加上目录前缀,保存到数组中
stringstr="./plugin/";
str+=pentry->d_name;
vstrPluginNames.push_back(str);
}
//关闭目录
closedir(dir);
returntrue;
}
测试程序源文件,test.cpp:
#include
#include
#include"CPluginSeacher.h"
usingnamespacestd;
//主程序,测试遍历目录,调用所有动态链接库
intmain()
{
//存放所有动态链接库的文件名
vectorvstrPluginNames;
//定义插件搜索类对象
CPluginSeacherenumerator;
//获取所有的插件文件名
if(!
enumerator.GetPluginNames(vstrPluginNames))
{
cout<<"GetPluginNameserror"<return0;
}
//遍历文件名数组,依次打开并调用
for(inti=0;i{
//打开动态链接库文件
void*handle=dlopen(vstrPluginNames[i].c_str(),RTLD_LAZY);
if(handle==0)
{
cout<<"dlopenerror"<return0;
}
//定义一个函数指针类型
typedefvoid(*FUNC_PRINT)();
//映射打印函数地址
FUNC_PRINTdl_print=(FUNC_PRINT)dlsym(handle,"Print");
if(dl_print==0)
{
cout<<"dlsymerror"<return0;
}
//调用打印函数
(dl_print)();
//卸载动态链接库
dlclose(handle);
}
return0;
}
先编译测试程序
再编译两个插件(动态链接库)
把两个插件放到plugin目录
运行测试程序,正确调用了所有插件中定义的打印函数
⏹版本3要求:
⏹版本2是同时调用所有插件的打印功能,现在要求一次只调用一种功能
⏹3-1:
通过命令行方式:
./a.outhelp,输出所有插件实现的功能ID,以及该功能ID对应的功能描述(参考代码3)
⏹3-2:
通过命令行方式:
./a.outFuncID,调用具体打印功能(每个插件导出GetID接口)
思想:
每一个插件要导出三个函数,分别是Print、Help和GetID。
程序启动后,加载所有插件,把它们的导出函数的地址用三个vector分别保存起来。
然后根据用户的输入,进行对应的调用。
插件1源代码,print_world.cpp
#include
usingnamespacestd;
//打印函数定义
extern"C"voidPrint()
{
cout<<"HelloWorld!
"<}
//帮助函数
extern"C"voidHelp()
{
cout<<"Thisfunctionprints'HelloWorld!
'"<}
//返回插件功能ID
extern"C"intGetID()
{
return1;
}
插件2源代码,print_china.cpp
#include
usingnamespacestd;
//打印函数定义
extern"C"voidPrint()
{
cout<<"HelloChina!
"<}
//帮助函数
extern"C"voidHelp()
{
cout<<"Thisfunctionprints'HelloChina!
'"<}
//返回插件功能ID
extern"C"intGetID()
{
return2;
}
插件文件搜索类的代码与上相同,省略。
。
。
测试程序代码,test.cpp
#include
#include
#include
#include
#include"CPluginSeacher.h"
usingnamespacestd;
//打印函数指针类型
typedefvoid(*FUNC_PRINT)();
//帮助函数指针类型
typedefvoid(*FUNC_HELP)();
//获取ID函数的指针类型
typedefint(*FUNC_GETID)();
//主程序,测试代码
intmain(intargc,char*argv[])
{
//存放所有插件的文件名
vectorvstrPluginNames;
//定义插件搜索类对象
CPluginSeacherenumerator;
//获取所有的插件文件名
if(!
enumerator.GetPluginNames(vstrPluginNames))
{
cout<<"GetPluginNameserror"<return0;
}
intpluginCount=0;
vectorvecPrint;
vectorvecHelp;
vectorvecGetid;
//遍历文件名数组,依次打开并获取导出函数地址
for(inti=0;i{
//打开动态链接库文件
void*handle=dlopen(vstrPluginNames[i].c_str(),RTLD_LAZY);
if(handle==0)
{
cout<<"dlopenerror"<return0;
}
//获取打印函数地址
vecPrint.push_back((FUNC_PRINT)dlsym(handle,"Print"));
//获取帮助印函数地址
vecHelp.push_back((FUNC_HELP)dlsym(handle,"Help"));
//获取ID函数地址
vecGetid.push_back((FUNC_GETID)dlsym(handle,"GetID"));
++pluginCount;
}
//获取用户输入的参数
if(argc>1){
char*param=argv[1];
if(!
strcmp(param,"help"))//用户输入的是help
{
//循环,显示所有插件的功能ID和帮助信息
for(inti=0;i{
cout<";//输出ID
vecHelp[i]();//调用帮助信息函数
}
}
else//用户可能输入的是功能ID
{
intid=atoi(param);
//循环,找出该功能
for(inti=0;iif(vecGetid[i]()==id)//找到了
vecPrint[i]();//调用其打印函数
}
}
return0;
}
先编译测试程序
编译插件1
编译插件2
将两个插件放入plugin目录
测试帮助信息
调用功能1
调用功能2
⏹版本4要求:
⏹在版本3中,插件导出了Print、GetID、Help三个函数,主程序需要使用多个容器分别保存这些函数地址
⏹在复杂的业务逻辑中,导出的函数可能更多,若还按照版本3的方式,代码维护性不佳
⏹将三个导出函数都放在一个类中,让插件外部获取该类的对象
思想:
定义一个插件接口类,所有插件类继承自这个接口类,并覆盖其中的三个方法,然后动态链接库的导出函数,返回类型为父类指针类型,创建一个插件类对象,返回其指针即可。
插件接口类代码,CPluginInterface.h
#ifndefCPLUGININTERFACE_H_
#defineCPLUGININTERFACE_H_
classCPluginInterface
{
public:
virtualvoidPrint(){};
virtualvoidHelp(){};
virtualintGetID(){return0;};
CPluginInterface(){};
~CPluginInterface(){};
};
#endif
插件1代码,print_world.cpp
#include
#include"CPluginInterface.h"
usingnamespacestd;
classPlugin:
publicCPluginInterface{
public:
Plugin()
{
}
virtualvoidPrint()
{
cout<<"HelloWorld!
"<}
virtualvoidHelp()
{
cout<<"Thisfunctionprints'HelloWorld!
'"<}
virtualintGetID()
{
return1;
}
};
//获取插件接口对象
extern"C"CPluginInterface*getInterface()
{
staticPluginplugin;
return&plugin;
}
插件2代码,print_china.cpp
#include
#include"CPluginInterface.h"
usingnamespacestd;
classPlugin:
publicCPluginInterface{
public:
Plugin()
{
}
virtualvoidPrint()
{
cout<<"HelloChina!
"<}
virtualvoidHelp()
{
cout<<"Thisfunctionprints'HelloChina!
'"<}
virtualintGetID()
{
return2;
}
};
//获取插件接口对象
extern"C"CPluginInterface*getInterface()
{
staticPluginplugin;
return&plugin;
}
插件文件搜索类的代码与上相同,省略。
。
。
测试程序代码,test.cpp
#include
#include
#include
#include
#include"CPluginSeacher.h"
#include"CPluginInterface.h"
usingnamespacestd;
//导出函数的指针类型
typedefCPluginInterface*(*FUNC_GETINTERFACE)();
//主程序,测试代码
intmain(intargc,char*argv[])
{
//存放所有插件的文件名
vectorvstrPluginNames;
//定义插件搜索类对象
CPluginSeacherenumerator;
//获取所有的插件文件名
if(!
enumerator.GetPluginNames(vstrPluginNames))
{
cout<<"GetPluginNameserror"<return0;
}
intpluginCount=0;
vectorvecPluginIfs;
//遍历文件名数组,依次打开并获取导出的接口对象
for(inti=0;i{
//打开动态链接库文件
void*handle=dlopen(vstrPluginNames[i].c_str(),RTLD_LAZY);
if(handle==0)
{
cout<<"dlopenerror"<return0;
}
//获取导出的接口对象指针
FUNC_GETINTERFACEfunc=(FUNC_GETINTERFACE)dlsym(handle,"getInterface");
//获取接口对象指针
CPluginInterface*pInterface=func();
//指针存到数组
vecPluginIfs.push_back(pInterface);
++pluginCount;
}
//获取用户输入的参数
if(argc>1){
char*param=argv[1];
if(!
strcmp(param,"help"))//用户输入的是help
{
//循环,显示所有插件的功能ID和帮助信息
for(inti=0;i{
cout<GetID()<<":
";//调用输出ID
vecPluginIfs[i]->Help();//调用帮助信息函数
}
}
else//用户可能输入的是功能ID
{
intid=atoi(param);
//循环,找出该功能
for(inti=0;iif(vecPluginIfs[i]->GetID()==id)//找到了
vecPluginIfs[i]->Print();//调用其打印函数
}
}
return0;
}
先编译测试程序
编译插件1和2
把插件放入plugin目录
测试帮助函数
测试插件1的功能
测试插件2的功能
六、实验步骤:
PPT上的4个版本程序
七、总结及心得体会:
经过本次实验,我学习了Linux动态链接库的编写和使用,学习了插件框架的基本设计方法。
八、对本实验过程及方法、手段的改进建议:
报告评分:
指导教师签字: