有些时候我们在一个工程项目中,并不希望将我们已经编写好的动态库中的子类的定义和方法暴露给其他的程序员,但是在其他的程序模块中,当需要我们在动态库中声明的子类的时候我们需要怎么办捏?
这就用到了我们今天需要总结的dlopen和dlsym这两个函数了!
函数定义:- #include<dlfcn.h>
- void * dlopen( const char * pathname, int mode)
在dlopen()函数以指定模式打开指定的动态链接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。
mode是打开方式,其值有多个,不同操作系统上实现的功能有所不同,在linux下,按功能可分为三类:
1、解析方式:RTLD_LAZY:在dlopen返回前,对于动态库中的未定义的符号不执行解析(只对函数引用有效,对于变量引用总是立即解析)。
RTLD_NOW: 需要在dlopen返回前,解析出所有未定义符号。
2、作用范围,可与解析方式通过“|”组合使用: RTLD_GLOBAL:动态库中定义的符号可被其后打开的其它库重定位。
RTLD_LOCAL: 与RTLD_GLOBAL作用相反,动态库中定义的符号不能被其后打开的其它库重定位。如果没有指明是RTLD_GLOBAL还是RTLD_LOCAL,则缺省为RTLD_LOCAL。
3、作用方式:RTLD_NODELETE: 在dlclose()期间不卸载库,并且在以后使用dlopen()重新加载库时不初始化库中的静态变量。这个flag不是POSIX-2001标准。
RTLD_NOLOAD:
不加载库。可用于测试库是否已加载(dlopen()返回NULL说明未加载,否则说明已加载),也可用于改变已加载库的flag,如:先前加载库的
flag为RTLD_LOCAL,用dlopen(RTLD_NOLOAD|RTLD_GLOBAL)后flag将变成RTLD_GLOBAL。这个
flag不是POSIX-2001标准。
RTLD_DEEPBIND:在搜索全局符号前先搜索库内的符号,避免同名符号的冲突。这个flag不是POSIX-2001标准。
返回值:
打开错误返回NULL
成功,返回库引用
编译时候要加入 -ldl (指定dl库)
函数定义:- #include<dlfcn.h>
- void *dlsym(void *handle,const char*symbol)
handle是由打开动态链接库后返回的指针,symbol就是要求获取的函数或全局变量的名称,函数返回值是void*,指向函数的地址,供调用使用。
函数定义:
- #include<sys/types.h>
- #include<dirent.h>
- DIR *opendir(const char *path)
- struct dirent* readdir(DIR* dir_handle);
该函数的作用就是打开一个文件夹的目录,并且返回一个目录流,这个时候我们需要用到readir()函数,来读取这个目录流中的内容,返回一个dirent的对象,这样我们就能够从dirent的结构体中获取到此是我们目录下的文件名!
下面来举一个例子来说明一下,大致是这样的,我现在这边有一个项目,首先定义了一个用于监控的接口,将很多监控方法的实现封装在了多个动态链接库的文件之中,并且每个库文件返回一个子类的对象(为一个子类定义的全局变量),我先在将这些动态的链接库的文件放在了一个固定的目录下,程序通过opendir(),readdir()函数获取到此目录下我们需要的动态链接库的文件名,从而得到文件的绝对路径,然后通过dlopen()函数加载每一个动态链接库,通过dlsym()函数获取该动态链接库返回的子类对象或者函数的指针,然后将这些个函数地址放入一个map中,通过监控指标的名称进行索引,从而调用不同的子类实现。
这是头文件中定义的通用的接口:
- #ifndef JSFPROBER_H
- #define JSFPROBER_H
- #include<string>
- using namespace std;
- class JsfProber
- {
- public:
- virtual double probe(){}
- virtual double probe(const pid_t pid){}
- string name;
- };
- JsfProber* getProber();
- #endif
一个子类的实现用于监控cpu使用率:
- #include<iostream>
- #include<vector>
- #include<fstream>
- #include"jsfprober.h"
- using namespace std;
- class CpurateProber : public JsfProber
- {
- public:
- CpurateProber();
- void readToVector(vector<double> &dvec,ifstream &in);
- double calcCpuRate(vector<double> &dvec);
- double probe();
- private:
- string name;
- double ltotal;
- double lbusy;
- };
- CpurateProber::CpurateProber()
- {
- name = "Cpurate";
- ltotal = 0;
- lbusy = 0;
- }
- void CpurateProber::readToVector(vector<double> &dvec,ifstream &in)
- {
- double temp;
- string s;
- in>>s;//skip the first string of the line;
- //cout<<s<<endl;
- for(int i = 0;i <= 3; i++)
- {
- in>>temp;
- //cout<<temp<<" ";
- dvec.push_back(temp);
- }
- }
- double CpurateProber::calcCpuRate(vector<double> &dvec)
- {
- double rate = 0.0;
- double ctotal = dvec[0] + dvec[1] + dvec[2] + dvec[3];
- double cbusy = dvec[0] + dvec[1] + dvec[2];
- rate = 100 * (cbusy - lbusy) / (ctotal - ltotal);
- lbusy = cbusy;
- ltotal = ctotal;
- return rate;
- }
- double CpurateProber::probe()
- {
- vector<double> dvec;
- double rate = 0.0;
- ifstream in("/proc/stat");
- if(!in)
- {
- cout<<"the file can not be used!!"<<endl;
- }
- readToVector(dvec,in);
- rate = calcCpuRate(dvec);
- cout<<"the cpu rate is:"<<rate<<"%"<<endl;
- return rate;
- }
- JsfProber* getProber()
- {
- JsfProber* p = new CpurateProber();
- return p;
- }
另外一个子类的实现用于监控内存使用率:
- #include<fstream>
- #include<vector>
- #include<iostream>
- #include"jsfprober.h"
- using namespace std;
- typedef double T;
- class MemrateProber : public JsfProber
- {
- public:
- T probe();
- void readToVector(vector<T> &tvec,ifstream &in);
- T calcMemRate(vector<T> &tvec);
- T freeMem(vector<T> &tvec);
- string name;
- };
- void MemrateProber::readToVector(vector<T> &tvec,ifstream &in)
- {
- string s;
- T temp;
- in>>s;
- in>>temp;
- tvec.push_back(temp);
- in>>s;
- in>>s;
- in>>temp;
- tvec.push_back(temp);
- }
- T MemrateProber::calcMemRate(vector<T> &tvec)//calc the rate of the used mem
- {
- T rate ;
- rate = 100 * (tvec[0] - tvec[1]) / tvec[0];
- return rate;
- }
- T MemrateProber::freeMem(vector<T> &tvec)//return the free mem mount
- {
- T free;
- free = tvec[1]/1024/1024;
- return free;
- }
- T MemrateProber::probe()
- {
- name="memrate";
- vector<T> tvec;
- T temp,free;
- ifstream in("/proc/meminfo");
- if(!in)
- {
- cout<<"this file can not be opened!"<<endl;
- }
- else
- {
- readToVector(tvec,in);
- temp = calcMemRate(tvec);
- free = freeMem(tvec);
- cout<<"the memory using rate is:"<<temp<<"%"<<endl;
- cout<<"the free mem is:"<<free<<"GB"<<endl;
- }
- return temp;
- }
- JsfProber * getProber ()
- {
- JsfProber * p = new MemrateProber ();
- return p;
- }
大家可以看到,在两个子类实现中别重新定义了头文件中声明的虚函数probe()完成了不同的监控功能,并且为了能够使程序能够统一的调用,分别返回了一个基类对象的指针。我将这两个子类的实现分别打包在了:libCpurate.so和libMemrate.so这两个动态库中。在另外一个程序main.cpp中我定义这样的一个map
m_prober,这样就可以实现当其他的程序有监控需求时,我很就可以查找这个map根据indicename来匹配索引通过Jsfprober*这样的基类指针来实现相应的监控!但是现在有这样的一个问题,就我在编译我的main.cpp时,因为看不错子类的动态库文件,所以编译通过不了,这里就用到了我们这里写到的dlopen函数:下面我只把这个这个简单实现的部分写一下,感兴趣的可以再联系我:
- JsfProber *(* func)(void);
- DIR *p = NULL;
- void *s = NULL;
- struct dirent *entry;
- string path = "/home/mini/桌面/jsf/src/plugin/prober/";
- if((p = opendir(path.c_str())) == NULL)
- {
- perror("opendir()");
- exit(-1);
- }
- while((entry = readdir(p)) != NULL)
- {
- if(!strcmp(entry->d_name,"libcpurate.so"))
- {
- path += "libcpurate.so";
- if((s = dlopen(path.c_str(), RTLD_LAZY)) == NULL)
- {
- perror("dlopen()");
- exit(-1);
- }
- else
- {
- func = (JsfProber *(*)(void))dlsym(s, "getProber");
- m_probers.insert(make_pair("Cpurate", func()));
- dlclose(s);
- }
- }
- else if(!strcmp(entry->d_name,"libmemrate.so"))
- {
- path += "libmemrate.so";
- if((s = dlopen(path.c_str(), RTLD_LAZY)) == NULL)
- {
- perror("dlopen()");
- exit(-1);
- }
- else
- {
- func = (JsfProber *(*)(void))dlsym(s, "getProber");
- m_probers.insert(make_pair("Memrate", func()));
- dlclose(s);
- }
- }
- }
现在总结一下吧:在这篇博文中我们想要总结是:如何在不暴露动态库中的内容的情况下来使用动态库中实现的方法,当然代码中涉及到了:如何利用c++读取指定目录下的所有文件的文件名,以及如何在linux系统下用c++来监控CPU和内存的使用率,以及虚函数和多态的实现等一些问题,大家如果感兴趣可以参考!
阅读(3158) | 评论(0) | 转发(0) |