>>转载请注明来源:
    
      飘零的代码 piao2010 ’s blog
    
    ,谢谢!^_^
    
     >>本文链接地址:
    
      Linux共享库(so)动态加载和升级
    
    
  
    学习Linux共享库动态加载缘于一个生产环境升级apache so文件常见错误操作:apache在运行中直接cp覆盖目标so文件,一段时间后错误日志里面出现关键词:
    
      Segmentation fault (段错误) 
    
    ,一个个worker进程就这样渐渐退出,最后无法处理HTTP请求。
    
     首先了解一下共享库的创建,
    
    
    源文件test.c
  
| 
              
                #include<stdio.h>
              
              
                #include<unistd.h>
              
              
 
              
                void
              
               test1
              
                (
              
              
                void
              
              
                )
              
              
                {
              
              
                
                  printf
                
              
              
                (
              
              
                "This is do test1
                
                  \n
                
                "
              
              
                )
              
              
                ;
              
              
    sleep
              
                (
              
              
                10
              
              
                )
              
              
                ;
              
              
                
                  printf
                
              
              
                (
              
              
                "End of test1
                
                  \n
                
                "
              
              
                )
              
              
                ;
              
              
                }
              
              
 
              
                void
              
               test2
              
                (
              
              
                void
              
              
                )
              
              
                {
              
              
                
                  printf
                
              
              
                (
              
              
                "This is do test2
                
                  \n
                
                "
              
              
                )
              
              
                ;
              
              
    sleep
              
                (
              
              
                10
              
              
                )
              
              
                ;
              
              
                
                  printf
                
              
              
                (
              
              
                "End of test2
                
                  \n
                
                "
              
              
                )
              
              
                ;
              
              
                }
              
             | 
    执行gcc -fPIC -shared -o libtest.so test.c 会生成共享库文件 libtest.so
    
     参数含义:
    
     -fPIC/-fpic: Compiler directive to output position independent code, a characteristic required by shared libraries. 创建共享库必须的参数
    
     -shared: Produce a shared object which can then be linked with other objects to form an executable.
  
然后使用共享库:源文件main2.c
| 
              
                #include <stdio.h>
              
              
 
              
                int
              
               main
              
                (
              
              
                )
              
              
                {
              
              
    test1
              
                (
              
              
                )
              
              
                ;
              
              
    test2
              
                (
              
              
                )
              
              
                ;
              
              
                return
              
              
                0
              
              
                ;
              
              
                }
              
             | 
    动态库链接:gcc -o main2 -L . -ltest main2.c 生成二进制程序main2
    
     参数含义:
    
     -L 指定动态库目录为当前目录
    
     -l 指定动态库名test,不要写libtest.so
  
    执行main2程序发现报错:
    
      error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory
    
    
     原因是共享库不在系统默认的路径里面,可以在shell执行
    
      export LD_LIBRARY_PATH=./ 
    
    添加当前路径或者在 
    
      /etc/ld.so.conf 增加路径并ldconfig生效
    
    。
    
     执行main2成功输出:
    
     This is do test1
    
     End of test1
    
     This is do test2
    
     End of test2
  
接下来是主角:动态加载,源文件main.c
| 
              
                #include <stdio.h>
              
              
                #include <dlfcn.h> /* 必须加这个头文件 */
              
              
 
              
                int
              
               main
              
                (
              
              
                )
              
              
                {
              
              
                void
              
              
                *
              
              lib_handle
              
                ;
              
              
                void
              
              
                (
              
              
                *
              
              fn1
              
                )
              
              
                (
              
              
                void
              
              
                )
              
              
                ;
              
              
                void
              
              
                (
              
              
                *
              
              fn2
              
                )
              
              
                (
              
              
                void
              
              
                )
              
              
                ;
              
              
                char
              
              
                *
              
              error
              
                ;
              
              
 
   lib_handle 
              
                =
              
               dlopen
              
                (
              
              
                "libtest.so"
              
              
                ,
              
               RTLD_LAZY
              
                )
              
              
                ;
              
              
                if
              
              
                (
              
              
                !
              
              lib_handle
              
                )
              
              
                {
              
              
      fprintf
              
                (
              
              stderr
              
                ,
              
              
                "%s
                
                  \n
                
                "
              
              
                ,
              
               dlerror
              
                (
              
              
                )
              
              
                )
              
              
                ;
              
              
                return
              
              
                1
              
              
                ;
              
              
                }
              
              
 
   fn1 
              
                =
              
               dlsym
              
                (
              
              lib_handle
              
                ,
              
              
                "test1"
              
              
                )
              
              
                ;
              
              
                if
              
              
                (
              
              
                (
              
              error 
              
                =
              
               dlerror
              
                (
              
              
                )
              
              
                )
              
              
                !=
              
               NULL
              
                )
              
              
                {
              
              
      fprintf
              
                (
              
              stderr
              
                ,
              
              
                "%s
                
                  \n
                
                "
              
              
                ,
              
               error
              
                )
              
              
                ;
              
              
                return
              
              
                1
              
              
                ;
              
              
                }
              
              
 
   fn1
              
                (
              
              
                )
              
              
                ;
              
              
 
   fn2 
              
                =
              
               dlsym
              
                (
              
              lib_handle
              
                ,
              
              
                "test2"
              
              
                )
              
              
                ;
              
              
                if
              
              
                (
              
              
                (
              
              error 
              
                =
              
               dlerror
              
                (
              
              
                )
              
              
                )
              
              
                !=
              
               NULL
              
                )
              
              
                {
              
              
      fprintf
              
                (
              
              stderr
              
                ,
              
              
                "%s
                
                  \n
                
                "
              
              
                ,
              
               error
              
                )
              
              
                ;
              
              
                return
              
              
                1
              
              
                ;
              
              
                }
              
              
 
   fn2
              
                (
              
              
                )
              
              
                ;
              
              
 
   dlclose
              
                (
              
              lib_handle
              
                )
              
              
                ;
              
              
 
   
              
                return
              
              
                0
              
              
                ;
              
              
                }
              
             | 
    接口函数介绍:
    
     (1) dlopen
    
     函数原型:void *dlopen(const char *libname,int flag);
    
     功能描述:dlopen必须在dlerror,dlsym和dlclose之前调用,表示要将库装载到内存,准备使用。
    
     如果要装载的库依赖于其它库,必须首先装载依赖库。如果dlopen操作失败,返回NULL值;如果库已经被装载过,则dlopen会返回同样的句柄。
    
     参数中的libname一般是库的全路径,这样dlopen会直接装载该文件;如果只是指定了库名称,在dlopen会按照下面的机制去搜寻:
    
     a.根据环境变量LD_LIBRARY_PATH查找
    
     b.根据/etc/ld.so.cache查找
    
     c.查找依次在/lib和/usr/lib目录查找。
    
     flag参数表示处理未定义函数的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暂时不去处理未定义函数,先把库装载到内 存,等用到没定义的函数再说;RTLD_NOW表示马上检查是否存在未定义的函数,若存在,则dlopen以失败告终。
  
    (2) dlerror
    
     函数原型:char *dlerror(void);
    
     功能描述:dlerror可以获得最近一次dlopen,dlsym或dlclose操作的错误信息,返回NULL表示无错误。dlerror在返回错误信息的同时,也会清除错误信息。
  
    (3) dlsym
    
     函数原型:void *dlsym(void *handle,const char *symbol);
    
     功能描述:在dlopen之后,库被装载到内存。dlsym可以获得指定函数(symbol)在内存中的位置(指针)。
    
     如果找不到指定函数,则dlsym会返回NULL值。但判断函数是否存在最好的方法是使用dlerror函数,
  
    (4) dlclose
    
     函数原型:int dlclose(void *);
    
     功能描述:将已经装载的库句柄减一,如果句柄减至零,则该库会被卸载。如果存在析构函数,则在dlclose之后,析构函数会被调用。 
  
    编译gcc -o main main.c -ldl 生成二进制程序main,执行输出
    
     This is do test1
    
     End of test1
    
     This is do test2
    
     End of test2
  
    到这里共享库动态加载就介绍完了:)
    
     最后模拟一下升级so故障:
    
     在执行main的时候,趁sleep期间cp 另外的so文件覆盖libtest.so,一会就出现Segmentation fault。
    
    
      但是如果是mv 另外的so文件覆盖libtest.so,则无此问题,或者先rm libtest.so 再cp/mv 也不会有问题,因此升级方法就是这两种,当然最好是先停应用再升级。
    
    至于原因,可以参考我前一篇博客
    
      《Linux cp mv rm ln 命令对于 inode 和 dentry 的影响》
    
    。
    
    
    
    
      12.5更新:
    
    
     今天咨询了维扬同学,可以用strace观察程序运行期间的系统调用,发现有不少mmap操作:
  
| 
              省略前面
open
              
                (
              
              
                "/root/so/libtest.so"
              
              
                ,
              
               O_RDONLY
              
                )
              
              
                =
              
              
                3
              
              
read
              
                (
              
              
                3
              
              
                ,
              
              
                "
                
                  \177
                
                ELF
                
                  \1
                
                
                  \1
                
                
                  \1
                
                
                  \3
                
                
                  \0
                
                
                  \0
                
                
                  \0
                
                
                  \0
                
                
                  \0
                
                
                  \0
                
                
                  \0
                
                
                  \0
                
                
                  \3
                
                
                  \0
                
                
                  \3
                
                
                  \0
                
                
                  \1
                
                
                  \0
                
                
                  \0
                
                
                  \0
                
                
                  \240
                
                
                  \3
                
                
                  \0
                
                
                  \000
                
                4
                
                  \0
                
                
                  \0
                
                
                  \0
                
                "
              
              ...
              
                ,
              
              
                512
              
              
                )
              
              
                =
              
              
                512
              
              
brk
              
                (
              
              
                0
              
              
                )
              
              
                =
              
              
                0x8227000
              
              
brk
              
                (
              
              
                0x8248000
              
              
                )
              
              
                =
              
              
                0x8248000
              
              
fstat64
              
                (
              
              
                3
              
              
                ,
              
              
                {
              
              st_dev
              
                =
              
              makedev
              
                (
              
              
                253
              
              
                ,
              
              
                0
              
              
                )
              
              
                ,
              
               st_ino
              
                =
              
              
                17559
              
              
                ,
              
               st_mode
              
                =
              
              S_IFREG
              
                |
              
              
                0755
              
              
                ,
              
               st_nlink
              
                =
              
              
                1
              
              
                ,
              
               st_uid
              
                =
              
              
                0
              
              
                ,
              
               st_gid
              
                =
              
              
                0
              
              
                ,
              
               st_blksize
              
                =
              
              
                4096
              
              
                ,
              
               st_blocks
              
                =
              
              
                16
              
              
                ,
              
               st_size
              
                =
              
              
                4348
              
              
                ,
              
               st_atime
              
                =
              
              
                2012
              
              
                /
              
              
                05
              
              
                /
              
              
                13
              
              
                -
              
              
                14
              
              
                :
              
              
                13
              
              
                :
              
              
                18
              
              
                ,
              
               st_mtime
              
                =
              
              
                2012
              
              
                /
              
              
                05
              
              
                /
              
              
                13
              
              
                -
              
              
                14
              
              
                :
              
              
                13
              
              
                :
              
              
                01
              
              
                ,
              
               st_ctime
              
                =
              
              
                2012
              
              
                /
              
              
                05
              
              
                /
              
              
                13
              
              
                -
              
              
                14
              
              
                :
              
              
                13
              
              
                :
              
              
                01
              
              
                }
              
              
                )
              
              
                =
              
              
                0
              
              
mmap2
              
                (
              
              NULL
              
                ,
              
              
                5772
              
              
                ,
              
               PROT_READ
              
                |
              
              PROT_EXEC
              
                ,
              
               MAP_PRIVATE
              
                |
              
              MAP_DENYWRITE
              
                ,
              
              
                3
              
              
                ,
              
              
                0
              
              
                )
              
              
                =
              
              
                0x6e6000
              
              
mmap2
              
                (
              
              
                0x6e7000
              
              
                ,
              
              
                4096
              
              
                ,
              
               PROT_READ
              
                |
              
              PROT_WRITE
              
                ,
              
               MAP_PRIVATE
              
                |
              
              MAP_FIXED
              
                |
              
              MAP_DENYWRITE
              
                ,
              
              
                3
              
              
                ,
              
              
                0
              
              
                )
              
              
                =
              
              
                0x6e7000
              
              
close
              
                (
              
              
                3
              
              
                )
              
              
                =
              
              
                0
              
              
munmap
              
                (
              
              
                0xb7753000
              
              
                ,
              
              
                15020
              
              
                )
              
              
                =
              
              
                0
              
              
fstat64
              
                (
              
              
                1
              
              
                ,
              
              
                {
              
              st_dev
              
                =
              
              makedev
              
                (
              
              
                0
              
              
                ,
              
              
                11
              
              
                )
              
              
                ,
              
               st_ino
              
                =
              
              
                3
              
              
                ,
              
               st_mode
              
                =
              
              S_IFCHR
              
                |
              
              
                0620
              
              
                ,
              
               st_nlink
              
                =
              
              
                1
              
              
                ,
              
               st_uid
              
                =
              
              
                0
              
              
                ,
              
               st_gid
              
                =
              
              
                5
              
              
                ,
              
               st_blksize
              
                =
              
              
                1024
              
              
                ,
              
               st_blocks
              
                =
              
              
                0
              
              
                ,
              
               st_rdev
              
                =
              
              makedev
              
                (
              
              
                136
              
              
                ,
              
              
                0
              
              
                )
              
              
                ,
              
               st_
atime
              
                =
              
              
                2012
              
              
                /
              
              
                05
              
              
                /
              
              
                13
              
              
                -
              
              
                14
              
              
                :
              
              
                56
              
              
                :
              
              
                03
              
              
                ,
              
               st_mtime
              
                =
              
              
                2012
              
              
                /
              
              
                05
              
              
                /
              
              
                13
              
              
                -
              
              
                14
              
              
                :
              
              
                56
              
              
                :
              
              
                03
              
              
                ,
              
               st_ctime
              
                =
              
              
                2012
              
              
                /
              
              
                05
              
              
                /
              
              
                13
              
              
                -
              
              
                14
              
              
                :
              
              
                53
              
              
                :
              
              
                31
              
              
                }
              
              
                )
              
              
                =
              
              
                0
              
              
mmap2
              
                (
              
              NULL
              
                ,
              
              
                4096
              
              
                ,
              
               PROT_READ
              
                |
              
              PROT_WRITE
              
                ,
              
               MAP_PRIVATE
              
                |
              
              MAP_ANONYMOUS
              
                ,
              
              
                -
              
              
                1
              
              
                ,
              
              
                0
              
              
                )
              
              
                =
              
              
                0xb7756000
              
              
write
              
                (
              
              
                1
              
              
                ,
              
              
                "This is do test1
                
                  \n
                
                "
              
              
                ,
              
              
                17
              
              
                )
              
              
                =
              
              
                17
              
              
rt_sigprocmask
              
                (
              
              SIG_BLOCK
              
                ,
              
              
                [
              
              CHLD
              
                ]
              
              
                ,
              
              
                [
              
              
                ]
              
              
                ,
              
              
                8
              
              
                )
              
              
                =
              
              
                0
              
              
rt_sigaction
              
                (
              
              SIGCHLD
              
                ,
              
               NULL
              
                ,
              
              
                {
              
              SIG_DFL
              
                ,
              
              
                [
              
              
                ]
              
              
                ,
              
              
                0
              
              
                }
              
              
                ,
              
              
                8
              
              
                )
              
              
                =
              
              
                0
              
              
rt_sigprocmask
              
                (
              
              SIG_SETMASK
              
                ,
              
              
                [
              
              
                ]
              
              
                ,
              
               NULL
              
                ,
              
              
                8
              
              
                )
              
              
                =
              
              
                0
              
              
nanosleep
              
                (
              
              
                {
              
              
                10
              
              
                ,
              
              
                0
              
              
                }
              
              
                ,
              
              
                0xbfd63fe4
              
              
                )
              
              
                =
              
              
                0
              
              
write
              
                (
              
              
                1
              
              
                ,
              
              
                "End of test1
                
                  \n
                
                "
              
              
                ,
              
              
                13
              
              
                )
              
              
                =
              
              
                13
              
              
write
              
                (
              
              
                1
              
              
                ,
              
              
                "This is do test2
                
                  \n
                
                "
              
              
                ,
              
              
                17
              
              
                )
              
              
                =
              
              
                17
              
              
rt_sigprocmask
              
                (
              
              SIG_BLOCK
              
                ,
              
              
                [
              
              CHLD
              
                ]
              
              
                ,
              
              
                [
              
              
                ]
              
              
                ,
              
              
                8
              
              
                )
              
              
                =
              
              
                0
              
              
rt_sigaction
              
                (
              
              SIGCHLD
              
                ,
              
               NULL
              
                ,
              
              
                {
              
              SIG_DFL
              
                ,
              
              
                [
              
              
                ]
              
              
                ,
              
              
                0
              
              
                }
              
              
                ,
              
              
                8
              
              
                )
              
              
                =
              
              
                0
              
              
rt_sigprocmask
              
                (
              
              SIG_SETMASK
              
                ,
              
              
                [
              
              
                ]
              
              
                ,
              
               NULL
              
                ,
              
              
                8
              
              
                )
              
              
                =
              
              
                0
              
              
nanosleep
              
                (
              
              
                {
              
              
                10
              
              
                ,
              
              
                0
              
              
                }
              
              
                ,
              
              
                0xbfd63fe4
              
              
                )
              
              
                =
              
              
                0
              
              
                ---
              
               SIGSEGV 
              
                (
              
              Segmentation fault
              
                )
              
               @ 
              
                0
              
              
                (
              
              
                0
              
              
                )
              
              
                ---
              
              
                +++
              
               killed by SIGSEGV 
              
                +++
              
             | 
    SIGSEGV信号估计和mmap只读映射之后写入(覆盖)文件有关?
    
     详见续篇
    
      《为何cp覆盖进程的动态库(so)会导致coredump》
    
    。
  
    参考资料:
    
     http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html
    
     http://hi.baidu.com/luoxsbupt/item/a9d346b7653a2771254b09bc
  


 
					 
					