一只简单的网络爬虫(基于linux C/C++)————支持动态模块加载
来源:互联网 发布:西方建筑欣赏知乎 编辑:程序博客网 时间:2024/06/11 00:46
插件在软件设计中有很大的好处,可以方便地扩展各种功能,使用插件技术能够在分析、设计、开发、项目计划、协作生产和产品扩展等很多方面带来好处:
(1)结构清晰、易于理解。由于借鉴了硬件总线的结构,而且各个插件之间是相互独立的,所以结构非常清晰也更容易理解。
(2)易修改、可维护性强。由于插件与宿主程序之间通过接口联系,就像硬件插卡一样,可以被随时删除,插入和修改,所以结构很灵活,容易修改,方便软件的升级和维护。
(3)可移植性强、重用力度大。因为插件本身就是由一系列小的功能结构组成,而且通过接口向外部提供自己的服务,所以复用力度更大,移植也更加方便。
(4)结构容易调整。系统功能的增加或减少,只需相应的增删插件,而不影响整个体系结构,因此能方便的实现结构调整。:
(5)插件之间的耦合度较低。由于插件通过与宿主程序通信来实现插件与插件,插件与宿主程序间的通信,所以插件之间的耦合度更低。
(6)可以在软件开发的过程中修改应用程序。由于采用了插件的结构,可以在软件的开发过程中随时修改插件,也可以在应用程序发行之后,通过补丁包的形式增删插件,通过这种形式达到修改应用程序的目的。
(7)灵活多变的软件开发方式。可以根据资源的实际情况来调整开发的方式,资源充足可以开发所有的插件,资源不充足可以选择开发部分插件,也可以请第三方的厂商开发,用户也可以根据自己的需要进行开发。
linux下面和动态连接相关的函数是dlopen、dlsym和dlclose
使用时在dlopen()函数以指定模式打开指定的动态链接库文件,并返回一个句柄给dlsym()的调用进程。使用dlclose()来卸载打开的库
dlopen
功能:打开一个动态链接库
包含头文件: dlfcn.h
函数定义: void * dlopen( const char * pathname, int mode );
函数描述: 在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。
mode:模式有下面这些
RTLD_LAZY 暂缓决定,等有需要时再解出符号
RTLD_NOW 立即决定,返回前解除所有未决定的符号。
RTLD_LOCAL
RTLD_GLOBAL 允许导出符号
RTLD_GROUP
RTLD_WORLD
返回值: 打开错误返回NULL ,成功,返回库引用 ,编译时候要加入 -ldl (指定dl库)
dlsym
功能:根据动态链接库操作句柄与符号,返回符号对应的地址。
包含头文件:dlfcn.h
函数定义:void*dlsym(void* handle,const char* symbol)
函数描述:dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数或全局变量的名称。
dlclose
dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
在使用的dlsym的时候,可如下所示:
假设在my.so中定义了一个void mytest()函数,那在使用my.so时先声明一个函数指针:void(*pMytest)();接下来先将那个my.so载入:pHandle=dlopen("my.so",RTLD_LAZY);//详见dlopen函数然后使用dlsym函数将函数指针 pMytest 指向 mytest() 函数:pMytest=(void(*)())dlsym(pHandle,"mytest");//可见放在双引号中的mytest不用加括号,即使有参数也不用(可调用dlerror();返回错误信息,正确返回为空)
这是单个函数的情况,比较容易理解,符号(symbol)就是我们要加载的函数名称。那么如果有多个函数呢?难道需要多次使用该函数加载动态库里面的函数吗???因为dlopen()加载库中的符号,这些符号在编译的时候是不知道的,而且这个符号(symbol)并没有说一定要是函数,因此我们可以使用结构体或其他数据结构把若干函数封装。然后获取该结构体,便可以使用里面的函数了。需要注意的是,我们在使用dlsym加载符号(symbol)的时候,这个符号(symbol)的名称一定要和我们在模块中的结构体的名称一样。爬虫的动态加载如下所示:
//动态加载模块//路径,模块名称,路径必须是绝对路径Module * dso_load(const char *path, const char *name)//模块名称{ void *rv = NULL; void *handle = NULL; Module *module = NULL; //字符串连接 char * npath = strcat2(3, path, name, ".so");//path+name+.so //dlopen以指定模式打开指定的动态链接库文件, //并返回一个句柄给dlsym()的调用进程。使用dlclose()来卸载打开的库 //编译时候要加入 -ldl (指定dl库) if ((handle = dlopen(npath, RTLD_GLOBAL | RTLD_NOW)) == NULL) { SPIDER_LOG(SPIDER_LEVEL_ERROR, "Load module fail(dlopen): %s", dlerror()); } //void*dlsym(void*handle,constchar*symbol) //handle:由dlopen打开动态链接库后返回的指针; //symbol:要求获取的函数或全局变量的名称。 //返回值:void* 指向函数的地址,供调用使用。 if ((rv = dlsym(handle, name)) == NULL) { SPIDER_LOG(SPIDER_LEVEL_ERROR, "Load module fail(dlsym): %s", dlerror()); } // name也是结构体的名称,因此可以返回一个结构体 module = (Module *)rv; module->init(module);//载入的时候调用init函数 return module;}
其中Module结构体如下:
//模块描述结构typedef struct Module{ int version;//主版本号 int minor_version;//次版本号 const char *name;//模块名称 void (*init)(Module *);//初始化函数 int (*handle)(void *);//处理函数} Module;
我们只需将所有的函数封装在该结构体内,然后返回一个这样的结构体,就可以通过该结构体使用里面的函数
下面看一个简单的动态模块的例子:
#include "dso.h"#include "url.h"static int handler(void * data) { Surl *url = (Surl *)data; if (url->level > g_conf->max_depth) return MODULE_ERR; return MODULE_OK;}static void init(Module *mod){ SPIDER_ADD_MODULE_PRE_SURL(mod);}Module maxdepth = { STANDARD_MODULE_STUFF, init, handler};
该模块实现了两个函数,然后封装在模块maxdepth中,因此dlsym(handle, name)中的name就应该是maxdepth,才可以正确返回该符号,最好是再使用(Module *)进行一下类型转换。
- 一只简单的网络爬虫(基于linux C/C++)————支持动态模块加载
- 一只简单的网络爬虫(基于linux C/C++)————开篇
- 一只简单的网络爬虫(基于linux C/C++)————配置文件设计及读取
- 一只简单的网络爬虫(基于linux C/C++)————守护进程
- 一只简单的网络爬虫(基于linux C/C++)————socket相关及HTTP
- 一只简单的网络爬虫(基于linux C/C++)————线程相关
- 一只简单的网络爬虫(基于linux C/C++)————利用正则表达式解析页面
- 一只简单的网络爬虫(基于linux C/C++)————主事件流程
- 一只简单的网络爬虫(基于linux C/C++)————浅谈并发(IO复用)模型
- 一只简单的网络爬虫(基于linux C/C++)————读取命令行参数及日志宏设计
- 一只简单的网络爬虫(基于linux C/C++)————Url处理以及使用libevent进行DNS解析
- python网络爬虫入门(一)——简单的博客爬虫
- eCos模块动态加载支持——objloader组件
- Linux C语言程序设计(二十)——基于Linux C的简单web服务器
- 网络爬虫—动态代理的使用
- 基于人人网的简单爬虫(一)——正则表达式
- linux模块编程(一)——加载你的模块
- linux模块编程(一)——加载你的模块
- 栈与递归实现Hanoi塔问题
- MMD_2c_FrequentItemsets
- 一个Java对象到底占用多大内存
- git将本地仓库上传到远程仓库
- python实现smtp发送邮件类-直接调用就好
- 一只简单的网络爬虫(基于linux C/C++)————支持动态模块加载
- 计算器类app应用体验
- linux服务器病毒
- myeclipse中tomcat 报异常总结
- 人机交互
- Base64以及Md5的使用
- Handlebars基本使用
- Hadoop的内存配置
- WIFI 探针原理