示例程序整体结构如下。
源程序:ps.c,cm.c,ps_tran.c,cm_tran.c(以上分版本1,版本2),main.c,makefile,aix,makefile.linux。
目标程序:libps.so,libcm.so,libps_tran.so,libcm_tran.so(以上分版本1,版本2),main。
版本1和版本2区别在于显示的字符串分别为ver1和ver2。这样运行时可以明确区分。下面以版本1展示各源程序。
ps.c:ps基础模块程序,提供组件(psfunc,psfunc2)给交易和其他模块(cm模块)调用。编译成libps.so。
-
#include <stdio.h>
-
-
extern void cmfunc2();
-
-
void psfunc()
-
{
-
printf("in psfunc()...ver1\n");
-
cmfunc2();
-
}
-
void psfunc2()
-
{
-
printf("in psfunc2()...ver1\n");
-
}
cm.c:cm基础模块程序,提供组件(cmfunc,cmfunc2)给交易和其他模块(ps模块)调用。编译成libcm.so。
-
#include <stdio.h>
-
-
extern void psfunc2();
-
-
void cmfunc()
-
{
-
printf("in cmfunc()...ver1\n");
-
psfunc2();
-
}
-
-
void cmfunc2()
-
{
-
printf("in cmfunc2()...ver1\n");
-
}
ps_tran.c:ps模块所属交易程序(ps_tran),调用ps组件和cm组件。编译成libps_tran.so。
-
#include <stdio.h>
-
-
extern void cmfunc();
-
extern void psfunc();
-
-
void ps_tran()
-
{
-
printf("in ps_tran()...ver1\n");
-
psfunc();
-
cmfunc();
-
}
cm_tran.c:ps模块所属交易程序(cm_tran),调用cm组件和ps组件。编译成libcm_tran.so。
-
#include <stdio.h>
-
-
extern void cmfunc();
-
extern void psfunc();
-
-
void cm_tran()
-
{
-
printf("in cm_tran()...ver1\n");
-
cmfunc();
-
psfunc();
-
}
main.c:主程序,调用交易程序(ps_tran和cm_tran),要求主程序控制加载动态库。编译成main。
-
#include <stdio.h>
-
#include <signal.h>
-
#include <dlfcn.h>
-
#include <stdlib.h>
-
#include <unistd.h>
-
-
int pauseflg=0;
-
int changeflg=0;
-
void *handle_ps;
-
void *handle_cm;
-
void *handle_pstran;
-
void *handle_cmtran;
-
-
void sigusr1(int signo)
-
{
-
pauseflg=1;
-
signal(SIGUSR1,sigusr1);
-
}
-
-
void sigusr2(int signo)
-
{
-
changeflg=1;
-
pauseflg=0;
-
signal(SIGUSR2,sigusr2);
-
}
-
-
void loadlib()
-
{
-
handle_ps=dlopen("libps.so",RTLD_LAZY|RTLD_GLOBAL);
-
if (handle_ps==NULL)
-
{
-
printf("open libps.so error:%s\n",dlerror());
-
exit(-1);
-
}
-
handle_cm=dlopen("libcm.so",RTLD_LAZY|RTLD_GLOBAL);
-
if (handle_cm==NULL)
-
{
-
printf("open libcm.so error:%s\n",dlerror());
-
exit(-1);
-
}
-
handle_pstran=dlopen("libps_tran.so",RTLD_LAZY|RTLD_GLOBAL);
-
if (handle_pstran==NULL)
-
{
-
printf("open libps_tran.so error:%s\n",dlerror());
-
exit(-1);
-
}
-
handle_cmtran=dlopen("libcm_tran.so",RTLD_LAZY|RTLD_GLOBAL);
-
if (handle_cmtran==NULL)
-
{
-
printf("open libcm_tran.so error:%s\n",dlerror());
-
exit(-1);
-
}
-
}
-
-
void closelib()
-
{
-
if (dlclose(handle_ps)!=0)
-
{
-
printf("close libps.so error:%s\n",dlerror());
-
exit(-1);
-
}
-
if (dlclose(handle_cm)!=0)
-
{
-
printf("close libcm.so error:%s\n",dlerror());
-
exit(-1);
-
}
-
if (dlclose(handle_pstran)!=0)
-
{
-
printf("close libps_tran.so error:%s\n",dlerror());
-
exit(-1);
-
}
-
if (dlclose(handle_cmtran)!=0)
-
{
-
printf("close libcm_tran.so error:%s\n",dlerror());
-
exit(-1);
-
}
-
}
-
-
void refreshlib()
-
{
-
closelib();
-
loadlib();
-
}
-
-
int main()
-
{
-
void (*handle)();
-
-
loadlib();
-
signal(SIGUSR1,sigusr1);
-
signal(SIGUSR2,sigusr2);
-
for (;;)
-
{
-
while (pauseflg)
-
{
-
printf("paused!\n");
-
sleep(1);
-
}
-
if (changeflg)
-
{
-
refreshlib();
-
changeflg=0;
-
}
-
handle=(void (*)())dlsym(RTLD_NEXT,"ps_tran");
-
if (handle==NULL)
-
{
-
printf("sym pstran error:%s\n",dlerror());
-
exit(-1);
-
}
-
handle();
-
handle=(void (*)())dlsym(RTLD_NEXT,"cm_tran");
-
if (handle==NULL)
-
{
-
printf("sym cmtran error:%s\n",dlerror());
-
exit(-1);
-
}
-
handle();
-
printf("-------------------------\n");
-
sleep(1);
-
}
-
closelib();
-
return 0;
-
}
总体来说,层次上分为三层,main.c为主控层,ps_tran.c和cm_tran.c为交易层,ps.c和cm.c为组件层。ps和cm模块可以相互调用组件,由各模块组件组合成交易,主控只调用交易。模块的组件和交易,是可以不断修改更新的,因此要求按模块实现动态加载更新。但是,更新时,要保证同一笔交易内,程序的版本一致性。即,要么都是使用的更新后的版本,要么都是使用更新前的版本。
ps和cm模块写法上,按照普通的外部函数使用方式,仅需要extern引入其他模块的组件声明,即可实现调用。main主程序以dlopen方式打开所有库,然后以dlsym方式查找到交易函数并调用。注意的是,dlopen时,选择RTLD_LAZY|RTLD_GLOBAL,说明延后进行符号完整性验证,以及将动态库内的函数设为全局可用(对其他动态库可见)。dlsym时,使用RTLD_NEXT在全局可用范围内自动搜索指定的函数,而无需明确指出函数究竟在哪个动态库中定义。但是,由于动态库间存在相互依赖关系,会导致dlclose延迟关闭,因此更新动态库程序时,应当关闭所有的动态库,全部重新打开。
linux下动态库对动态库依赖关系的搜索实现较好,动态库中通过extern调用其他动态库函数时,无需指出函数在哪个动态库中,而是RTLD_LAZY控制在dlsym运行时搜索。因此,linux环境的makefile如下所示。另外值得注意的是,main函数使用了RTLD_NEXT,因此默认用了g++编译。如果要用gcc编译,则需指定编译参数-D_GNU_SOURCE。
-
main: main.c
-
@echo "make main"
-
g++ -g -rdynamic -o main main.c -ldl
-
#gcc -g -rdynamic -D_GNU_SOURCE -o main main.c -ldl
-
ver1:ps1.c cm1.c ps_tran1.c cm_tran1.c
-
@echo "make ver1"
-
gcc -g -shared -fPIC -o libps.so.1 ps1.c
-
gcc -g -shared -fPIC -o libcm.so.1 cm1.c
-
gcc -g -shared -fPIC -o libps_tran.so.1 ps_tran1.c
-
gcc -g -shared -fPIC -o libcm_tran.so.1 cm_tran1.c
-
ln -sf libps_tran.so.1 libps_tran.so
-
ln -sf libcm_tran.so.1 libcm_tran.so
-
ln -sf libps.so.1 libps.so
-
ln -sf libcm.so.1 libcm.so
-
ver2:ps2.c cm2.c ps_tran2.c cm_tran2.c
-
@echo "make ver2"
-
gcc -g -shared -fPIC -o libps.so.2 ps2.c
-
gcc -g -shared -fPIC -o libcm.so.2 cm2.c
-
gcc -g -shared -fPIC -o libps_tran.so.2 ps_tran2.c
-
gcc -g -shared -fPIC -o libcm_tran.so.2 cm_tran2.c
-
ln -sf libps_tran.so.2 libps_tran.so
-
ln -sf libcm_tran.so.2 libcm_tran.so
-
ln -sf libps.so.2 libps.so
-
ln -sf libcm.so.2 libcm.so
AIX环境对动态库依赖关系支持有不足,系统对RTLD_LAZY尚未完整支持,因而extern函数需要在编译时指明连接所属的动态库,这样dlopen加载时会按照依赖关系连带加载,否则dlopen时会报错extern的函数未找到。编译时如果有交叉依赖,只能把动态库编译两次,先不连接其他动态库编译一次,再连接其他动态库编译一次。解决先有鸡还是先有蛋的问题。makefile如下所示。
-
main: main.c
-
@echo "make main"
-
xlc -g -brtl -o main main.c -ldl
-
ver1:ps1.c cm1.c ps_tran1.c cm_tran1.c
-
@echo "make ver1"
-
xlc -g -G -bdynamic -brtl -bnoentry -o libps.so.1 ps1.c
-
xlc -g -G -bdynamic -brtl -bnoentry -o libcm.so.1 cm1.c
-
xlc -g -G -bdynamic -brtl -bnoentry -o libps_tran.so.1 ps_tran1.c
-
xlc -g -G -bdynamic -brtl -bnoentry -o libcm_tran.so.1 cm_tran1.c
-
ln -sf libps_tran.so.1 libps_tran.so
-
ln -sf libcm_tran.so.1 libcm_tran.so
-
ln -sf libps.so.1 libps.so
-
ln -sf libcm.so.1 libcm.so
-
xlc -g -G -bdynamic -brtl -bnoentry -o libps.so.1 ps1.c -L. -lcm
-
xlc -g -G -bdynamic -brtl -bnoentry -o libcm.so.1 cm1.c -L. -lps
-
xlc -g -G -bdynamic -brtl -bnoentry -o libps_tran.so.1 ps_tran1.c -L. -lps -lcm
-
xlc -g -G -bdynamic -brtl -bnoentry -o libcm_tran.so.1 cm_tran1.c -L. -lps -lcm
-
chmod go-rwx *.so.1
-
ver2:ps2.c cm2.c ps_tran2.c cm_tran2.c
-
@echo "make ver2"
-
xlc -G -bdynamic -brtl -bnoentry -o libps.so.2 ps2.c
-
xlc -G -bdynamic -brtl -bnoentry -o libcm.so.2 cm2.c
-
xlc -G -bdynamic -brtl -bnoentry -o libps_tran.so.2 ps_tran2.c
-
xlc -G -bdynamic -brtl -bnoentry -o libcm_tran.so.2 cm_tran2.c
-
ln -sf libps_tran.so.2 libps_tran.so
-
ln -sf libcm_tran.so.2 libcm_tran.so
-
ln -sf libps.so.2 libps.so
-
ln -sf libcm.so.2 libcm.so
-
xlc -G -bdynamic -brtl -bnoentry -o libps.so.2 ps2.c -L. -lcm
-
xlc -G -bdynamic -brtl -bnoentry -o libcm.so.2 cm2.c -L. -lps
-
xlc -G -bdynamic -brtl -bnoentry -o libps_tran.so.2 ps_tran2.c -L. -lps -lcm
-
xlc -G -bdynamic -brtl -bnoentry -o libcm_tran.so.2 cm_tran2.c -L. -lps -lcm
-
chmod go-rwx *.so.2
综上所示,main主程序通过SIGUSR2信号,触发在tran调用循环的间隔处,进行所有动态库的重新关闭加载,这样就可以达到完美的运行时更新效果,也保证了程序写法上的简便。
阅读(2812) | 评论(1) | 转发(0) |