Chinaunix首页 | 论坛 | 博客

pacman2000的ChinaUnix博客

在编程的影子传说

分类: C/C++

2017-06-29 08:15:20

    示例程序整体结构如下。
    源程序: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。 

  1. #include <stdio.h>

  2. extern void cmfunc2();

  3. void psfunc()
  4. {
  5.     printf("in psfunc()...ver1\n");
  6.     cmfunc2();
  7. }
  8. void psfunc2()
  9. {
  10.     printf("in psfunc2()...ver1\n");
  11. }


    cm.c:cm基础模块程序,提供组件(cmfunc,cmfunc2)给交易和其他模块(ps模块)调用。编译成libcm.so。

  1. #include <stdio.h>

  2. extern void psfunc2();

  3. void cmfunc()
  4. {
  5.     printf("in cmfunc()...ver1\n");
  6.     psfunc2();
  7. }

  8. void cmfunc2()
  9. {
  10.     printf("in cmfunc2()...ver1\n");
  11. }

    ps_tran.c:ps模块所属交易程序(ps_tran),调用ps组件和cm组件。编译成libps_tran.so。
  1. #include <stdio.h>

  2. extern void cmfunc();
  3. extern void psfunc();

  4. void ps_tran()
  5. {
  6.     printf("in ps_tran()...ver1\n");
  7.     psfunc();
  8.     cmfunc();
  9. }

     cm_tran.c:ps模块所属交易程序(cm_tran),调用cm组件和ps组件。编译成libcm_tran.so。
  1. #include <stdio.h>

  2. extern void cmfunc();
  3. extern void psfunc();

  4. void cm_tran()
  5. {
  6.     printf("in cm_tran()...ver1\n");
  7.     cmfunc();
  8.     psfunc();
  9. }

     main.c:主程序,调用交易程序(ps_tran和cm_tran),要求主程序控制加载动态库。编译成main。
  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <dlfcn.h>
  4. #include <stdlib.h>
  5. #include <unistd.h>

  6. int pauseflg=0;
  7. int changeflg=0;
  8. void *handle_ps;
  9. void *handle_cm;
  10. void *handle_pstran;
  11. void *handle_cmtran;

  12. void sigusr1(int signo)
  13. {
  14.     pauseflg=1;
  15.     signal(SIGUSR1,sigusr1);
  16. }

  17. void sigusr2(int signo)
  18. {
  19.     changeflg=1;
  20.     pauseflg=0;
  21.     signal(SIGUSR2,sigusr2);
  22. }

  23. void loadlib()
  24. {
  25.     handle_ps=dlopen("libps.so",RTLD_LAZY|RTLD_GLOBAL);
  26.     if (handle_ps==NULL)
  27.     {
  28.         printf("open libps.so error:%s\n",dlerror());
  29.         exit(-1);
  30.     }
  31.     handle_cm=dlopen("libcm.so",RTLD_LAZY|RTLD_GLOBAL);
  32.     if (handle_cm==NULL)
  33.     {
  34.         printf("open libcm.so error:%s\n",dlerror());
  35.         exit(-1);
  36.     }
  37.     handle_pstran=dlopen("libps_tran.so",RTLD_LAZY|RTLD_GLOBAL);
  38.     if (handle_pstran==NULL)
  39.     {
  40.         printf("open libps_tran.so error:%s\n",dlerror());
  41.         exit(-1);
  42.     }
  43.     handle_cmtran=dlopen("libcm_tran.so",RTLD_LAZY|RTLD_GLOBAL);
  44.     if (handle_cmtran==NULL)
  45.     {
  46.         printf("open libcm_tran.so error:%s\n",dlerror());
  47.         exit(-1);
  48.     }
  49. }

  50. void closelib()
  51. {
  52.     if (dlclose(handle_ps)!=0)
  53.     {
  54.         printf("close libps.so error:%s\n",dlerror());
  55.         exit(-1);
  56.     }
  57.     if (dlclose(handle_cm)!=0)
  58.     {
  59.         printf("close libcm.so error:%s\n",dlerror());
  60.         exit(-1);
  61.     }
  62.     if (dlclose(handle_pstran)!=0)
  63.     {
  64.         printf("close libps_tran.so error:%s\n",dlerror());
  65.         exit(-1);
  66.     }
  67.     if (dlclose(handle_cmtran)!=0)
  68.     {
  69.         printf("close libcm_tran.so error:%s\n",dlerror());
  70.         exit(-1);
  71.     }
  72. }

  73. void refreshlib()
  74. {
  75.     closelib();
  76.     loadlib();
  77. }

  78. int main()
  79. {
  80.     void (*handle)();

  81.     loadlib();
  82.     signal(SIGUSR1,sigusr1);
  83.     signal(SIGUSR2,sigusr2);
  84.     for (;;)
  85.     {
  86.         while (pauseflg)
  87.         {
  88.             printf("paused!\n");
  89.             sleep(1);
  90.         }
  91.         if (changeflg)
  92.         {
  93.             refreshlib();
  94.             changeflg=0;
  95.         }
  96.         handle=(void (*)())dlsym(RTLD_NEXT,"ps_tran");
  97.         if (handle==NULL)
  98.         {
  99.             printf("sym pstran error:%s\n",dlerror());
  100.             exit(-1);
  101.         }
  102.         handle();
  103.         handle=(void (*)())dlsym(RTLD_NEXT,"cm_tran");
  104.         if (handle==NULL)
  105.         {
  106.             printf("sym cmtran error:%s\n",dlerror());
  107.             exit(-1);
  108.         }
  109.         handle();
  110.         printf("-------------------------\n");
  111.         sleep(1);
  112.     }
  113.     closelib();
  114.     return 0;
  115. }



    总体来说,层次上分为三层,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。

  1. main: main.c
  2. @echo "make main"
  3. g++ -g -rdynamic -o main main.c -ldl
  4. #gcc -g -rdynamic -D_GNU_SOURCE -o main main.c -ldl
  5. ver1:ps1.c cm1.c ps_tran1.c cm_tran1.c
  6. @echo "make ver1"
  7. gcc -g -shared -fPIC -o libps.so.1 ps1.c
  8. gcc -g -shared -fPIC -o libcm.so.1 cm1.c
  9. gcc -g -shared -fPIC -o libps_tran.so.1 ps_tran1.c
  10. gcc -g -shared -fPIC -o libcm_tran.so.1 cm_tran1.c
  11. ln -sf libps_tran.so.1 libps_tran.so
  12. ln -sf libcm_tran.so.1 libcm_tran.so
  13. ln -sf libps.so.1 libps.so
  14. ln -sf libcm.so.1 libcm.so
  15. ver2:ps2.c cm2.c ps_tran2.c cm_tran2.c
  16. @echo "make ver2"
  17. gcc -g -shared -fPIC -o libps.so.2 ps2.c
  18. gcc -g -shared -fPIC -o libcm.so.2 cm2.c
  19. gcc -g -shared -fPIC -o libps_tran.so.2 ps_tran2.c
  20. gcc -g -shared -fPIC -o libcm_tran.so.2 cm_tran2.c
  21. ln -sf libps_tran.so.2 libps_tran.so
  22. ln -sf libcm_tran.so.2 libcm_tran.so
  23. ln -sf libps.so.2 libps.so
  24. ln -sf libcm.so.2 libcm.so


    AIX环境对动态库依赖关系支持有不足,系统对RTLD_LAZY尚未完整支持,因而extern函数需要在编译时指明连接所属的动态库,这样dlopen加载时会按照依赖关系连带加载,否则dlopen时会报错extern的函数未找到。编译时如果有交叉依赖,只能把动态库编译两次,先不连接其他动态库编译一次,再连接其他动态库编译一次。解决先有鸡还是先有蛋的问题。makefile如下所示。

  1. main: main.c
  2. @echo "make main"
  3. xlc -g -brtl -o main main.c -ldl
  4. ver1:ps1.c cm1.c ps_tran1.c cm_tran1.c
  5. @echo "make ver1"
  6. xlc -g -G -bdynamic -brtl -bnoentry -o libps.so.1 ps1.c
  7. xlc -g -G -bdynamic -brtl -bnoentry -o libcm.so.1 cm1.c
  8. xlc -g -G -bdynamic -brtl -bnoentry -o libps_tran.so.1 ps_tran1.c
  9. xlc -g -G -bdynamic -brtl -bnoentry -o libcm_tran.so.1 cm_tran1.c
  10. ln -sf libps_tran.so.1 libps_tran.so
  11. ln -sf libcm_tran.so.1 libcm_tran.so
  12. ln -sf libps.so.1 libps.so
  13. ln -sf libcm.so.1 libcm.so
  14. xlc -g -G -bdynamic -brtl -bnoentry -o libps.so.1 ps1.c -L. -lcm
  15. xlc -g -G -bdynamic -brtl -bnoentry -o libcm.so.1 cm1.c -L. -lps
  16. xlc -g -G -bdynamic -brtl -bnoentry -o libps_tran.so.1 ps_tran1.c -L. -lps -lcm
  17. xlc -g -G -bdynamic -brtl -bnoentry -o libcm_tran.so.1 cm_tran1.c -L. -lps -lcm
  18. chmod go-rwx *.so.1
  19. ver2:ps2.c cm2.c ps_tran2.c cm_tran2.c
  20. @echo "make ver2"
  21. xlc -G -bdynamic -brtl -bnoentry -o libps.so.2 ps2.c
  22. xlc -G -bdynamic -brtl -bnoentry -o libcm.so.2 cm2.c
  23. xlc -G -bdynamic -brtl -bnoentry -o libps_tran.so.2 ps_tran2.c
  24. xlc -G -bdynamic -brtl -bnoentry -o libcm_tran.so.2 cm_tran2.c
  25. ln -sf libps_tran.so.2 libps_tran.so
  26. ln -sf libcm_tran.so.2 libcm_tran.so
  27. ln -sf libps.so.2 libps.so
  28. ln -sf libcm.so.2 libcm.so
  29. xlc -G -bdynamic -brtl -bnoentry -o libps.so.2 ps2.c -L. -lcm
  30. xlc -G -bdynamic -brtl -bnoentry -o libcm.so.2 cm2.c -L. -lps
  31. xlc -G -bdynamic -brtl -bnoentry -o libps_tran.so.2 ps_tran2.c -L. -lps -lcm
  32. xlc -G -bdynamic -brtl -bnoentry -o libcm_tran.so.2 cm_tran2.c -L. -lps -lcm
  33. chmod go-rwx *.so.2



    综上所示,main主程序通过SIGUSR2信号,触发在tran调用循环的间隔处,进行所有动态库的重新关闭加载,这样就可以达到完美的运行时更新效果,也保证了程序写法上的简便。


阅读(84) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册