ACE的服务配置框架,是一个非常强大的框架,采用它可以使应用根据相应配置文件进行有选择的服务启动,应用可以重新配置其服务,而无需重新编译或是重新启动应用本身。对于电信领域中的应用,该框架功能确实是非常棒的优点。但是,对于静态类型的服务,也就是说相应的函数是静态的编译到应用中的,ACE一般在主程序中都要用ACE_STATIC_SVC_REQUIRE和ACE_STATIC_SVC_REGISTER来向ACE_Service_Repository登记静态服务,这就造成了主程序和服务之间还存在一定的耦合。当然,对于静态服务这么做没有任何问题。但是,这多多少少让我觉得不爽,这里我是一个极端主义者^_^,我想让我的主程序完全是通过配置文件来启动静态服务的,在代码上主程序和服务完全没有耦合!
在JAVA里,可以通过一个类的名字来获得该类本身。我不太熟悉JAVA^_^,JAVA中的反射大概是这个意思。为了满足上面的要求,主程序完全通过配置文件来启动静态服务,也就是说我们要根据函数的名字来得到相应的函数(对于通过共享库动态加载的服务,我们可以通过dlopen,dlsym等函数,很容易的从函数名字导出函数本身。ACE底层实现,也是通过这样的方式,来动态加载服务的),因此我在标题里写了C语言的“反射”^_^。在C和C++里,我们从语言的角度上,我们没有办法这样做。
俗话说:天无绝人之路。没有C做不到的事情(C是能做到,但是很多时候我做不到,因为我的水平还很差^_^)。这里我们可以通过解析ELF文件格式,很轻松的从函数的名字来得到函数本身,代码如下(该代码只是一个简单的演示):
文件名:parse.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libelf.h>
#include <gelf.h>
void test(int i)
{
printf("oh, come on baby\n");
printf("the i is %d\n",i);
}
int main(int argc, char **argv)
{
Elf *elf = NULL;
Elf_Scn *scn = NULL;
GElf_Shdr shdr;
Elf_Data *data = NULL;
int fd, ii, count;
void (*func)(int);
elf_version(EV_CURRENT);
fd = open(argv[1], O_RDONLY);
elf = elf_begin(fd, ELF_C_READ, NULL);
while ((scn = elf_nextscn(elf, scn)) != NULL) {
gelf_getshdr(scn, &shdr);
if (shdr.sh_type == SHT_SYMTAB) {
/* found a symbol table, go print it. */
break;
}
}
data = elf_getdata(scn, NULL);
count = shdr.sh_size / shdr.sh_entsize;
for (ii=0; ii < count; ++ii) {
GElf_Sym sym;
gelf_getsym(data, ii, &sym);
if(!strcmp("test", elf_strptr(elf, shdr.sh_link, sym.st_name))){
func = (void (*)(int))(unsigned long)(sym.st_value& 0xffffffff);//得到函数名字对应的函数地址
func(7);
}
}
elf_end(elf);
close(fd);
}
编译:gcc -o parse parse.c -lelf
运行:parse parse
通过上面这种手段,我们从函数的名字得到了相应的函数。该代码在solaris上编译运行通过。网络上通过解析ELF进行程序hack的例子不少,我这里简单的解析ELF,拿来正道之用,也可谓是名门正派了。(别仍鸡蛋呀^_^)
其实,很多东西就是一种思路,我没有看ACE的时候,从来没有想到过通过配置文件来进行服务的选择启动。当我看了ACE后,就说:“喔,原来程序可以这样”,并且通过自己的发散思维,可以进一步考虑出一些东西。哎。。。,看来自己要学的东西还很多呀,自己还是一个嫩黄瓜^_^。