示例程序整体结构如下。
源程序: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调用循环的间隔处,进行所有动态库的重新关闭加载,这样就可以达到完美的运行时更新效果,也保证了程序写法上的简便。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/283313/viewspace-2141362/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/283313/viewspace-2141362/