『 Linux 』动静态库的创建与使用 ( 万字纯干货 )


静态库

请添加图片描述

静态库是一种将一组目标文件打包在一起的文件;

用于在编译时链接到应用程序中;

静态库的文件的拓展名一般为.a(Linux/Unix中)或是.lib(windows中);

静态库的特点通常为以下几种:

  • 编译时链接

    静态库在编译时被链接到应用程序的可执行文件中;

    链接器将静态库中的代码和数据复制到最终的可执行文件中;

  • 独立性

    静态库在编译时被嵌入到应用程序中,因此生成的可执行文件不依赖于外部的库文件;

    使得可执行文件在运行时更加独立不需要额外的库文件;

  • 性能

    静态库在编译时已经被链接到可执行文件中,故运行时不需要动态加载库文件,因此启动速度较快,运行时性能较好;

  • 文件大小

    静态库的代码和数据被复制在可执行文件中,因此生成的可执行文件通常较大;


静态库的创建与使用

请添加图片描述

假设需要创建一个提供加减乘除方法的库;

  • 头文件(mymath.h)

    #pragma once
    
    extern int myerrno;
    
    int add(int x, int y);
    
    int sub(int x, int y);
    
    int mul(int x, int y);
    
    int div(int x, int y);
    
  • 源文件(mymath.c)

    #include "mymath.h"
    
    int myerrno = 0;
    
    int add(int x, int y) { return x + y; }
    
    int sub(int x, int y) { return x - y; };
    
    int mul(int x, int y) { return x * y; };
    
    int div(int x, int y) {
      if (y == 0) {
        myerrno = 1;
        return -1;
      }
      return x / y;
    };
    
  • 编译源文件为目标文件

    使用gcc编译源文件mymath.c为目标文件mymath.o;

    gcc -c mymath.c -o mymath.o
    
  • 创建静态库

    使用ar命令将目标文件mymath.o打包成静态库libmymath.a;

    ar rcs libmymath.a mymath.o
    

    单个目标文件也可以打包成静态库;

  • 静态库的使用

    创建一个测试程序main.c并使用静态库libmymath.a;

    #include <stdio.h>
    #include "mymath.h"
    
    int main() {
        int a = 10, b = 5;
        printf("Add: %d + %d = %d\n", a, b, add(a, b));
        printf("Sub: %d - %d = %d\n", a, b, sub(a, b));
        printf("Mul: %d * %d = %d\n", a, b, mul(a, b));
        printf("Div: %d / %d = %d\n", a, b, div(a, b));
        return 0;
    }
    
  • 编译和链接测试程序

    gcc -o main main.c -L. -lmymath
    

    其中-L .表示头文件在当前目录中,-lmymath表示名为mymath的库(libmymath.a需要去掉其中的lib前缀与后缀.a才为库名);

  • 运行测试程序

    $ ./main 
    Add: 10 + 5 = 15
    Sub: 10 - 5 = 5
    Mul: 10 * 5 = 50
    Div: 10 / 5 = 2
    

头文件为一个库的声明(类似于说明书);

库的使用必须提供其头文件;


利用Makefile创建静态库并使用

请添加图片描述

lib=libmymath.a

$(lib):mymath.o
#	ar -rc libmymath.a mymath.o 
# ar为归档工具 rc表示 replace and creat
	ar -rc $@ $^


mymath.o:mymath.c

	gcc -c $^

.PHONY:clean
clean:
	rm -rf *.a *.o lib

.PHONY:output
output:
	mkdir -p lib/include
	mkdir -p lib/mymathlib
	cp *.h lib/include
	cp *.a lib/mymathlib
  • lib=libmymath.a

    定义了一个变量lib,其值为libmymath.a;

    这个变量代表了要构建的静态库名称;

  • $(lib):mymath.o

    这个规则指定了构建静态库libmymath.a的方法;

    其中$(lib)表示一个变量的引用;

    它告诉make程序使用之前定义的lib变量的值;

    变量在makefile中用于存储会被多次使用的数据以方便修改和维护;

    例如在规则:

    $(lib):mymath.o
    	ar -rc $@ $^
    

    这里的$(lib)将会被替换为libmymath.a,故实际执行的规则为:

    libmymath.a: mymath.o
    	ar -rc $@ $^
    

    这表示了libmymath.a依赖于mymath.o;

  • ar -rc $@ $^

    这是个构建静态库的命令;

    其中ar是一个用于创建和修改归档文件(静态库)的工具;

    -rc选项表示如果归档文件不存在则创建;

    如果存在则替换里面的模块;

    $@表示当前规则的目标,这里代指libmymath.a;

    $^代表所有的依赖项,这里指mymath.o;

    • ar rcs $@ $^

      这与-rc选项相同;

      唯一不同的是:

      • ar -rc

        这里的-r选项表示将文件替换到静态库中(如果静态库中已经存在同名文件则替换;如果不存在则添加);

        -c选项表示如果指定的库文件不存在则创建静态库文件,一般情况下-c选项是默认行为;

      • ar rcs

        这里的rc-rc相同,不赘述;

        s选项作用是在创建或者更新库文件之后自动创建或者更新静态库的索引;

        这个索引是用于提高链接(使用改库的程序)的速度;

  • .PHONY:clean

    这里的.PHONY表示clean是一个伪目标;

    用于删除所有构建过程中生成的文件;

    包括静态库文件,对象文件和lib目录;

    .PHONY:clean
    clean:
    	rm -rf *.a *.o lib
    
  • .PHONY:output

    表示output是一个伪目标,用于创建一个存放构建结果的目录结构,并将头文件和静态库文件复制到相应的位置;

    .PHONY: output
    output:
        mkdir -p lib/include
        mkdir -p lib/mymathlib
        cp *.h lib/include
        cp *.a lib/mymathlib
    

    其中mkdir -p lib/includemkdir -p lib/mymathlib 分别创建lib/includelib/mymathlib目录,用于存放头文件和静态库文件;

    cp *.h lib/include 把所有头文件复制到lib/include目录下;

    cp *.a lib/mymathlib 把所有静态库文件复制到lib/mymathlib目录下;

假设将已经构建好的静态库打包发布给用户进行使用需要将make output后所生成的lib文件夹发送给用户;

假设存在一个test目录为用户目录,并且lib文件夹已经发送给用户;

$ mkdir test ; cp -r lib test ; cp main.c test ; ls test
lib  main.c

其中用户的源文件为main.c:

#include <stdio.h>

#include "mymath.h"

int main() {
  int a = 10, b = 5;
  printf("Add: %d + %d = %d\n", a, b, add(a, b));
  printf("Sub: %d - %d = %d\n", a, b, sub(a, b));
  printf("Mul: %d * %d = %d\n", a, b, mul(a, b));
  printf("Div: %d / %d = %d\n", a, b, div(a, b));
  return 0;
}

直接编译将产生报错,原因为未找到mymath.h头文件的定义;

$ gcc -o mytest main.c 
main.c:3:10: fatal error: mymath.h: No such file or directory
 #include "mymath.h"
          ^~~~~~~~~~
compilation terminated.

原因是找不到头文件mymath.h的定义;

在编译过程中将首先在默认路径下找头文件;

这两个路径一般为 当前路径/usr/include等目录;

故在进行编译链接时需要使用:

$ gcc -o mytest main.c -I lib/include/ -L lib/mymathlib/ -lmymath ; ls #ls为显示当前目录下的文件
lib  main.c  mytest
  • gcc -o mytest main.c

    最终生成的可执行文件为mytest;

  • -I lib/include/

    -I选项为include的简写;

    表示该库的头文件在lib/include/目录下;

    若头文件在当前目录下或者是与源文件在同一目录下即可省略-I选项;

  • -L lib/mymathlib/

    -L选项为link链接的简写;

    表示需要链接的库文件在lib/mymathlib/目录下;

    同样类似于标准库的库文件也已经在系统中被安装,若是没有指定目录则会去默认路径(当前路径或是系统安装库文件的目录)中查找;

  • -lmymath

    -l选项同样表示link;

    一个文件夹内可能存在多个库文件,需要让链接器知道具体需要链接哪个库(建议不需要加空格);

    如果依赖其他库则需要额外的-l选项指定其他库;

    这里表示需要链接mymath库(去除前缀lib与后缀.a即为库名);

    如果未使用-l指定库文件下进行编译(以当前为例):

    $ gcc -o mytest main.c -I lib/include/ -L lib/mymathlib/ 
    /tmp/ccelRikz.o: In function `main':
    main.c:(.text+0x21): undefined reference to `add'
    main.c:(.text+0x49): undefined reference to `sub'
    main.c:(.text+0x71): undefined reference to `mul'
    collect2: error: ld returned 1 exit status
    # 链接错误
    

    由于一个目录下可能存在多个库;

    不指定具体的库文件无法在声明中找到文件对应的定义故无法成功链接;

    在使用自己写的库与第三方库时必须使用-l选项指定需要链接的哪个库;

  • 为什么头文件只需要给定所在目录而库文件需要具体指定文件?

原因为头文件已经在源文件中进行声明,在此处为include "mymath.h",在编译过程中只需要头文件所在路径即可找到该头文件;


gcc/g++的链接行为

请添加图片描述

使用ldd命令可以查看程序的链接情况,但只能看到动态链接的链接情况;

$ ldd ./mytest 
    linux-vdso.so.1 =>  (0x00007ffd55bcf000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f359458c000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f359495a000)

在使用gcc/g++对某个库进行链接时默认采用的是动态链接;

动态链接的好处包括了减小了可执行文件的大小(因为不需要再文件中包含整个库的代码);

同时当库更新时,程序可以不做更改就直接使用新版本的库;

如果希望进行静态链接,即将库的代码直接包含在可执行文件中可使用-static选项;

这个选项告诉链接器尽可能使用静态库(.a文件)而不是动态库(.so文件);

但对于-static选项而言,这个选项并不保证所有的库都会进行静态链接;

当某个库只有动态版本可用,那么链接器可能无法满足静态链接的要求;

在某些情况下,如果用户只提供了静态库而没有提供对应的动态库,链接器将不得不使用静态链接.但这也取决于编译时的具体指令和可用的库;


不带路径使用第三方静态库

请添加图片描述

一般使用第三方库时在使用gcc/g++进行编译需要带头文件的路径与库文件的路径,如:

$ gcc -o mytest main.c -I lib/include/ -L lib/mymathlib/ 
  • -I表示头文件路径
  • -L表示库文件路径
  • -l表示具体需要链接的库(使用第三方库时无论如何不可省略)

一般具有两种方法:

  • 将第三方库安装至系统中(即将库拷贝至系统默认目录下)

    一般情况下在CentOS7.6中:

    • 头文件的默认搜索路径为/usr/include/

      $ sudo cp lib/include/mymath.h /usr/include/ ; ls /usr/include/mymath.h
      /usr/include/mymath.h
      
    • 库文件的默认搜索路径为/lib64/

      $ sudo cp lib/mymathlib/libmymath.a /lib64/  ; ls /lib64/libmymath.a
      /lib64/libmymath.a
      

    当再次进行编译链接时即可不指定库的路径直接进行链接(在使用第三方库时必须使用-l选项带库名);

    $ ls ; gcc -o mytest main.c -lmymath ; ls;./mytest 
    main.c    mymath.c  makefile  
    mymath.h  test
    
    main.c    mymath.c  mytest
    makefile  mymath.h  test
    
    Add: 10 + 5 = 15
    Sub: 10 - 5 = 5
    Mul: 10 * 5 = 50
    Div: 10 / 5 = -1
    myerrno = 1
    
    • 优点

      简化编译命令,不需要直接指定库的位置;

    • 缺点

      可能会污染系统环境,尤其是如果有多个版本的库可能会导致版本冲突;

  • 在默认搜索路径中建立软链接

    $ sudo ln -s /home/_XXX/Begin/my_-linux/Pro24/Dynamic_and_static_library/lib/include/ /usr/include/myinc
    
    $ sudo ln -s /home/_XXX/Begin/my_-linux/Pro24/Dynamic_and_static_library/lib/mymathlib/libmymath.a /lib64/libmymath.a
    
    $ sudo ls -a /lib64/libmymath.a -l
    lrwxrwxrwx 1 root root 91 Jun 12 16:23 /lib64/libmymath.a -> /home/_XXX/Begin/my_-linux/Pro24/Dynamic_and_static_library/lib/mymathlib/libmymath.a
    
    $ sudo ls -a /usr/include/myinc -l
    lrwxrwxrwx 1 root root 78 Jun 12 16:19 /usr/include/myinc -> /home/_XXX/Begin/my_-linux/Pro24/Dynamic_and_static_library/lib/include/
    

    当软链接建好之后在头文件之中即可以软连接/头文件的形式进行包含;

    #include "myinc/mymath.h";

    $ ls ; gcc -o mytest main.c -lmymath;ls
    lib  libmymath.a  main.c  makefile  mymath.c  mymath.h  mymath.o  test
    lib  libmymath.a  main.c  makefile  mymath.c  mymath.h  mymath.o  mytest  test
    $ ./mytest 
    Add: 10 + 5 = 15
    Sub: 10 - 5 = 5
    Mul: 10 * 5 = 50
    Div: 10 / 5 = -1
    myerrno = 1
    
    • 优点

      避免了直接复制文件可能带来的污染问题同时简化了编译命令;

    • 缺点

      需要管理软链接,如果原始库的位置发生变动需要重新更新链接;


动态库

请添加图片描述

动态库(Dynamic Library)又被称为共享库(Shared Library);

是一种包含了可由多个程序同时共享使用的代码和数据的文件;

与静态库不同,动态库在程序运行时被链接而不是在编译时;

这意味着程序在执行过程中会加载动态库并使用其中的代码和数据;

动态库的特点通常为一下几种:

  • 运行时链接

    动态库在应用程序运行时而非编译时被链接;

    这意味着动态库中的代码和数据不会被拷贝至应用程序的可执行文件中而是从外部库文件中动态加载;

  • 依赖性

    动态库在运行时被加载,因此生成的可执行文件依赖于外部的库文件;

  • 性能

    由于动态库在运行时加载,其对应的启动时间将会比静态库的时间较慢;

    但一旦加载,动态库函数的运行速度与静态库相当;

  • 文件大小

    动态库不会被整合到每个使用它的可执行文件当中;

    因此使用动态库的可执行文件通常比使用静态库的文件要小,同时因为这点其在内存中也可以减少总体消耗;


动态库的创建

请添加图片描述

在上文的基础上新建四个文件进行测试;

分别为:

  • myprintf.h

    #pragma once
    
    #include <stdio.h>
    
    void Print();
    
  • myprintf.c

    #include "myprintf.h"
    
    void Print() {
      printf("hello myprintf\n");
      printf("hello myprintf\n");
      printf("hello myprintf\n");
    }
    
  • mylog.h

    #pragma once
    
    #include <stdio.h>
    
    void Log();
    
  • mylog.c

    #pragma once
    
    #include <stdio.h>
    
    void Log(){
      printf("hello mylog\n");
      printf("hello mylog\n");
      printf("hello mylog\n");
    }
    

当前目录即为:

$ ls
main.c  makefile  mylog.c  mylog.h  mymath.c  mymath.h  myprintf.c  myprintf.h

其中mymath.cmymath.h用于生成静态库;

  • 编译源文件为目标文件

    使用gcc编译源文件myprintf.cmylog.c文件为目标文件myprintf.omylog.o;

    唯一的区别是在生成动态库的目标文件时需要添加-fPIC选项,即 与位置无关码 ;

    $ gcc -fPIC -c mylog.c ;gcc -fPIC -c myprintf.c
    
  • 将目标文件打包为动态库

    使用gcc/g++将目标文件myprintf.omylog.o打包成动态库libmymethod.so;

    在打包成动态库时使用gcc -o选项进行链接;

    但在链接过程中需要添加-shared选项告诉链接器所生成的并不是可执行文件而是一个动态库(共享库),其中shared即为共有;

    $ gcc -shared -o libmymethod.so myprin
    tf.o mylog.o
    $ ls
    libmymethod.so  makefile  mylog.h  mymath.c  myprintf.c  myprintf.o
    main.c          mylog.c   mylog.o  mymath.h  myprintf.h
    

    单个目标文件也可以打包成动态库;

动态库的使用在下文中进行阐述;


利用Makefile创建动态库

请添加图片描述

在上文的基础上对Makefile文件进行修改;

最终目的为生成一个静态库与一个动态库并进行打包;

dy-lib=libmymethod.so
static-lib=libmymath.a

.PHONY:all
all:$(dy-lib) $(static-lib)

$(static-lib):mymath.o
#	ar -rc libmymath.a mymath.o 
# ar为归档工具 rc表示 replace and creat
	ar -rc $@ $^ 

$(dy-lib):myprintf.o mylog.o
	gcc -shared -o $@ $^

myprintf.o:myprintf.c
	gcc -fPIC -c $^

mylog.o:mylog.c
	gcc -fPIC -c $^

mymath.o:mymath.c
	gcc -c $^

.PHONY:clean
clean:
	rm -rf *.a *.o *.so mylib

.PHONY:output
output:
	mkdir -p mylib/include
	mkdir -p mylib/lib
	cp *.h mylib/include
	cp *.a mylib/lib
	cp *.so mylib/lib
  • 变量定义

    dy-lib=libmymethod.so
    static-lib=libmymath.a
    

    这里定义了两个变量;

    dy-lib用于存储动态库的名称;

    static-lib用于存储静态库的名称;

  • 伪目标.PHONY

    .PHONY:all clean output
    

    .PHONY指示给定的目标是"伪目标",这意味着它们不代表文件名;

    这里定义了三个伪目标:all,clean,和output;

    使用伪目标可以防止Make与同名的文件冲突;

  • 主目标all

    all:$(dy-lib) $(static-lib)
    

    all是默认目标,通常是第一个目标;这里它依赖于两个库文件的目标,即$(dy-lib)$(static-lib);

    运行makemake all时,这两个库文件将被构建;

  • 静态库的构建

    $(static-lib):mymath.o
        ar -rc $@ $^ 
    

    这个规则用于静态库的构建;

    其依赖于mymath.o目标文件;

    使用ar命令将mymath.o归档成静态库$(static-lib)libmymath.a;

  • 动态库的构建

    $(dy-lib):myprintf.o mylog.o
        gcc -shared -o $@ $^
    

    这个规则用于构建动态库;

    其依赖于myprintf.omylog.o目标文件;

    使用gcc-shared选项将这些对象文件链接成动态库$(dy-lib)libmymethod.so;

  • 目标文件构建

    myprintf.o:myprintf.c
        gcc -fPIC -c $^
    
    mylog.o:mylog.c
        gcc -fPIC -c $^
    
    mymath.o:mymath.c
        gcc -c $^
    

    这些规则用于编译.c文件使其生成对应的.o目标文件;

    对于myprintf.omylog.o文件,因为其为动态库做准备需要使用-fPIC选项来生成与位置无关码;

  • 清理目标clean

    clean:
        rm -rf *.a *.o *.so mylib
    

    clean目标用于删除所有编译生成的文件,包括静态库,动态库,目标文件以及发布后的mylib目录文件;

  • 发布output

    output:
        mkdir -p mylib/include
        mkdir -p mylib/lib
        cp *.h mylib/include
        cp *.a mylib/lib
        cp *.so mylib/lib
    

    output目标用于创建发布目录结构并拷贝头文件,动静态库至其对应位置;


动态库的使用

请添加图片描述

假设将已经构建好的静态库打包发布给用户进行使用需要将make output后所生成的mylib文件夹发送给用户;

$ make clean ; make ;make output
rm -rf *.a *.o *.so mylib
gcc -fPIC -c myprintf.c
gcc -fPIC -c mylog.c
gcc -shared -o libmymethod.so myprintf.o mylog.o
gcc -c mymath.c
ar -rc libmymath.a mymath.o 
mkdir -p mylib/include
mkdir -p mylib/lib
cp *.h mylib/include
cp *.a mylib/lib
cp *.so mylib/lib

存在一个test目录为用户目录,并且mylib文件夹已经发送给用户;

$ mkdir test ; cp -r mylib test ; cp main.c test ; ls test
mylib  main.c

其中用户的源文件为main.c:

#include <stdio.h>

#include "mymath.h"
#include "myprintf.h"
#include "mylog.h"

int main() {
  int a = 10, b = 5;
  printf("Add: %d + %d = %d\n", a, b, add(a, b));
  printf("Sub: %d - %d = %d\n", a, b, sub(a, b));
  printf("Mul: %d * %d = %d\n", a, b, mul(a, b));
  int n = div(5, 0);
  printf("Div: %d / %d = %d\nmyerrno = %d\n", a, b, n,myerrno);
  printf("\n");

  Printf();
  Log();
  return 0;
}

使用gcc对两个库进行链接生成a.out可执行文件;

$ gcc main.c -I mylib/include/ -L mylib/lib/ -lmymath -lmymethod
$ ls
a.out  main.c  mylib

当需要链接的文件需要依赖多个库时需要使用多个-l来指定所使用的库;

若是头文件或者库路径也存在不同位置时则需要使用多个-L-I来确认路径;

直接执行a.out可执行文件时将发生报错;

$ ./a.out 
./a.out: error while loading shared libraries: libmymethod.so: cannot open shared object file: No such file or directory

使用ldd对该可执行文件观察其链接情况发现虽然使用动态链接但not fount;

$ ldd a.out 
    linux-vdso.so.1 =>  (0x00007ffca2fed000)
    libmymethod.so => not found
    libc.so.6 => /lib64/libc.so.6 (0x00007f21ba253000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f21ba621000)

在使用动态库时存在一个组件为动态链接器(Dynamic Linker),即加载器(Loader);

这个组件主要职责是在程序启动时或者在运行时按需动态加载库(.so文件)到内存中并处理程序中对库函数的调用;

加载器主要负责:

  • 查找程序运行时所需的动态库
  • 将这些库加载至内存中
  • 接续程序对这些库中函数和变量的引用以确保程序中的调用可以正确定位到苦衷相应的函数或者变量

程序在运行时加载器会根据一系列预设的规则和环境变量来查找动态库;

如果动态库不再这些预设的位置中,程序运行时则可能找不到这些库而导致运行失败;

通常有几种方法可以确保动态链接器能在运行时找到所需的动态库:

  • 将动态库放在标准库路径下(如/usr/lib64/)

    $ sudo cp mylib/lib/libmymethod.so /usr/lib64/
    

    使用ldd对可执行程序a.out观察其链接情况时发现已经可以观察到其动态链接的链接情况;

    $ ldd ./a.out 
        linux-vdso.so.1 =>  (0x00007ffe7a988000)
        libmymethod.so => /lib64/libmymethod.so (0x00007ff2e19e5000)
        libc.so.6 => /lib64/libc.so.6 (0x00007ff2e1617000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ff2e1be7000)
    

    同时运行可执行程序时将不再报错;

    $ ./a.out 
    Add: 10 + 5 = 15
    Sub: 10 - 5 = 5
    Mul: 10 * 5 = 50
    Div: 10 / 5 = -1
    myerrno = 1
    
    hello myprintf
    hello myprintf
    hello myprintf
    hello mylog
    hello mylog
    hello mylog
    
  • 在标准库中设立软链接

    与上文中的静态库的使用相同,将软链接设置到搜索的默认路径中;

    $ sudo ln -s /home/_XXX/Begin/my_-linux/Pro24/Dynamic_and_static_library/test/mylib/lib/libmymethod.so /lib64/libmymethod.so
    

    使用ldd时发现可执行文件a.out可找到链接情况同时运行不报错;

    $ ldd a.out 
        linux-vdso.so.1 =>  (0x00007fff79543000)
        libmymethod.so => /lib64/libmymethod.so (0x00007f07c92a7000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f07c8ed9000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f07c94a9000)
    $ ./a.out 
    Add: 10 + 5 = 15
    Sub: 10 - 5 = 5
    Mul: 10 * 5 = 50
    Div: 10 / 5 = -1
    myerrno = 1
    
    hello myprintf
    hello myprintf
    hello myprintf
    hello mylog
    hello mylog
    hello mylog
    
  • 将动态库的路径设置在环境变量LD_LIBRARY_PATH

    Linux中存在一个环境变量为LD_LIBRARY_PATH;

    这个环境变量用来存放用户的动态库;

    使用export即可将动态库的路径放置在环境变量LD_LIBRARY_PATH中(针对当前shell);

    $ export LD_LIBRARY_PATH=/home/_XXX/Begin/my_-linux/Pro24/Dynamic_and_static_library/test/mylib/lib/:$LD_LIBRARY_PATH
    
    #此处只需要更新到动态库所在路径即可
    

    若是需要使每次都能够将动态库路径放置在环境变量中应修改配置文件~/.bashrc或是~/.profile,在这两个配置文件中的其中一个添加上述export命令(上述命令为演示);

    使用ldd观察可执行文件可以查询到链接情况同时运行不报错;

    $ ldd a.out 
        linux-vdso.so.1 =>  (0x00007fff641c8000)
        libmymethod.so => /home/_XXX/Begin/my_-linux/Pro24/Dynamic_and_static_library/T0612/test/mylib/lib/libmymethod.so (0x00007f917fe41000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f917fa73000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f9180043000)
    $ ./a.out 
    Add: 10 + 5 = 15
    Sub: 10 - 5 = 5
    Mul: 10 * 5 = 50
    Div: 10 / 5 = -1
    myerrno = 1
    
    hello myprintf
    hello myprintf
    hello myprintf
    hello mylog
    hello mylog
    hello mylog
    

    当打开一个新的终端时使用ldd观察可执行文件情况则再次找不到链接属性;

    $ ldd ./a.out 
        linux-vdso.so.1 =>  (0x00007fff572f5000)
        libmymethod.so => not found
        libc.so.6 => /lib64/libc.so.6 (0x00007f7dae34c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f7dae71a000)
    
  • 在编译时使用-rpath选项

    在编译过程中可以使用-Wl选项;

    -Wl选项表示在编译命令中传递参数给链接器ld;

    同时在使用-Wl选项时可以使用-rpath选项并跟一个路径;

    表示编译时告诉链接器在运行时在哪个路径下搜索动态库;

    即:

    gcc -o my_program my_program.c -L/path/to/library -lmylib -Wl,-rpath,/path/to/library
    

    在这个命令中,-Wl,-rpath,/path/to/library的意思是将-rpath,/path/to/library这个参数传递给链接器;

    -Wl告诉gcc,后面跟着的是链接器的参数,-rpath,/path/to/library是具体的链接器参数,告诉链接器在运行时应该去哪个路径下搜索动态库;

    同样的在使用-Wl,-rpath时只需要跟到动态库所在路径即可;

    使用该方法编译链接可执行文件时该可执行文件将会把编译时所传递的路径作为默认的动态库链接路径(针对当前可执行文件),是一种长期行为且无关shell;

    只有该可执行文件或者是重新对该可执行文件进行链接并未带-Wl,-rpath选项时动态链接器才不会在运行该可执行文件时去指定的路径下寻找;

    例:

    $ gcc  main.c -I mylib/include/ -L mylib/lib/ -lmymath -lmymethod -Wl,-rpath,/home/_XXX/Begin/my_-linux/Pro24/Dynamic_and_static_
    library/T0612/test/mylib/lib/
    

    在使用-Wl,-rpath选项后使用ldd查看链接情况发现无异常并且可以直接运行不报错;

    $ ldd ./a.out 
        linux-vdso.so.1 =>  (0x00007fff0d5e0000)
        libmymethod.so => /home/_XXX/Begin/my_-linux/Pro24/Dynamic_and_static_library/T0612/test/mylib/lib/libmymethod.so (0x00007ff9253f1000)
        libc.so.6 => /lib64/libc.so.6 (0x00007ff925023000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ff9255f3000)
        
    $ ./a.out 
    Add: 10 + 5 = 15
    Sub: 10 - 5 = 5
    Mul: 10 * 5 = 50
    Div: 10 / 5 = -1
    myerrno = 1
    
    hello myprintf
    hello myprintf
    hello myprintf
    hello mylog
    hello mylog
    hello mylog
    
  • 将路径放置在动态链接器配置目录中

    Linux当中/etc/ld.so.conf.d路径为动态链接器配置目录;

    这里面包括所有默认的动态链接器配置目录;

    # su -
    # cd /etc/ld.so.conf.d/
    # ls
    bind-export-x86_64.conf
    kernel-3.10.0-1160.108.1.el7.x86_64.conf
    kernel-3.10.0-957.21.3.el7.x86_64.conf
    kernel-3.10.0-957.el7.x86_64.conf
    mysql-x86_64.conf
    

    其中的.conf即为配置文件;

    每个.conf文件中都存放了一串路径,路径即为动态链接器将会搜索的路径;

    # cat mysql-x86_64.conf 
    /usr/lib64/mysql
    

    可将需要的可执行文件所依赖的动态库在该路径下创建并设置;

    设置完毕之后执行ldconfig使得配置文件重新加载

    例:

    echo "/home/_XXX/Begin/my_-linux/Pro24/Dynamic_and_static_library/T0612/test/mylib/lib" > newtest.conf
    cat newtest.conf 
    /home/_XXX/Begin/my_-linux/Pro24/Dynamic_and_static_library/T0612/test/mylib/lib
    
    ldconfig
    

    设置好后回到原先路径使用ldd观察可执行文件可以查到链接状态且运行不报错;

    $ ldd a.out 
        linux-vdso.so.1 =>  (0x00007ffcfe5a8000)
        libmymethod.so => /home/_XXX/Begin/my_-linux/Pro24/Dynamic_and_static_library/T0612/test/mylib/lib/libmymethod.so (0x00007f2436a00000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f2436632000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f2436c02000)
    $ ./a.out 
    Add: 10 + 5 = 15
    Sub: 10 - 5 = 5
    Mul: 10 * 5 = 50
    Div: 10 / 5 = -1
    myerrno = 1
    
    hello myprintf
    hello myprintf
    hello myprintf
    hello mylog
    hello mylog
    hello mylog
    
  • 25
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dio夹心小面包

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值