【Linux】Linux下静态库(.a)和动态库(.so)的制作和使用

如有错误,欢迎大家指正,感谢!

​​​​​​​目录

 一、程序工作过程简单介绍

1. 安装GCC

2. 源程序工作流程

3. 使用示例

4. gcc常用的编译参数选项

二、静态库的制作及使用

1. 库是什么

2. 静态库的制作及使用

2.1. 制作静态库

2.2. 使用静态库

 2.3 静态库的制作和使用总结

三、 动态库的制作和使用

1 制作动态库

2. 使用动态库

2.1 动态库的工作原理

2.2 定位共享库

3.  动态库的制作和使用总结

四、 静态库和动态库的比较


本文讲解Linux下静态库和动态库的制作和使用,分如下部分:

  • 程序工作的过程;
  • 静态库的制作和使用;
  • 动态库的制作和使用;
  • 静态库和动态库的区别;

在介绍静态库和动态库的制作及使用之前,需要先了解程序的工作流程。

 一、程序工作过程简单介绍

1. 安装GCC

Linux下使用GCC对.c或.cpp文件进行编译链接生成可执行的文件。
简单理解,GCC就是一个编译器,把程序源文件编译成可执行文件。

GCC支持C的不同标准,可以使用命令行参数 -std=c99 决定编译时使用哪一个标准,如: -std=c99表示编译器支持C99标准。
安装GCC的命令(g++表示C++):

sudo apt install gcc g++

查看安装的GCC/G++版本可以使用如下命令:

gcc -v  
gcc --version
g++ -v
g++ --version

2. 源程序工作流程

源程序变成可执行程序的流程如下,高级语言经过编译生成汇编语言,汇编语言经过汇编变成计算机可以识别的机器语言,机器语言可以在计算机上直接运行。 

【Linux】Linux下静态库(.a)和动态库(.so)的制作和使用_第1张图片

 GCC工作流程:

源代码进行预处理(-E)变成预处理后源代码(.i),再经过编译器编译(-S)生成汇编代码(.s),汇编代码经过汇编器汇编(-c)成为目标代码(.o),目标代码与库代码、其它目标代码等一起经过链接器链接成可执行程序。

3. 使用示例

在Linux/lesson3/文件夹下新建test.c文件,文件内容如下:

#include 

int main()
{
    #ifdef DEBUG
    printf("This is DEBUG info\n");
    #endif
    // This is one-line comment
    printf("Hello Linux!\n");

    return 0;
}

1> 使用gcc命令【-E】进行预处理生成预处理文件(.i),并显示预处理后生成的文件。
                -E是预处理命令,-o指定生成的文件;
                ls 查看当前文件夹内的所有文件夹及文件;

~/Linux/lesson3$ gcc test.c -E -o test.i
~/Linux/lesson3$ ls
test.c  test.i

2> 使用gcc命令【-S】进行编译(只编译,不汇编)生成汇编代码(.s),并显示编译后的文件。
                -S 编译指定的源文件,不进行汇编,-o指定生成的文件;
                ls  查看当前文件夹内所有文件夹及文件

~/Linux/lesson3$ gcc test.i -S -o test.s
~/Linux/lesson3$ ls
test.c  test.i  test.s

3> 使用gcc命令【-c】进行编译、汇编,不进行链接,并显示编译、汇编后的文件。
                -c 编译、汇编指定的文件,但是不进行链接
                ls 查看当前文件夹内所有文件夹及文件

~/Linux/lesson3$ gcc test.s -c -o test.o
~/Linux/lesson3$ ls
test.c  test.i  test.o  test.s

4> 使用gcc命令【-o】链接指定的文件生成可执行文件,并运行可执行文件。
                -o [file1] [file2]或者 [file2] -o [file1] 把【文件file2】编译成【可执行文件file1】
                ls 查看当前文件夹内的所有文件夹及文件
                ./a.out 运行可执行文件

~/Linux/lesson3$ gcc test.o -o a.out
~/Linux/lesson3$ ls
a.out  test.c  test.i  test.o  test.s
~/Linux/lesson3$ ./a.out
Hello Linux!

4. gcc常用的编译参数选项

gcc编译选项 说明 示例
-E

预处理指定的源文件,不进行编译;

如果不指定生成的文件,会把预处理后的内容显示在终端;如果指定了生成的文件(如示例),会把预处理后的内容保存到指定的文件内

gcc test.c -E -o test.i
-S

编译指定的源文件,不进行汇编;

不指定生成的文件会把编译后的文件保存到【源文件名.s】中

gcc test.i -S

gcc test.i -S -o test.s

gcc test.c -S

gcc test.c -S -otest.s

-c

编译、汇编指定的源文件,不进行链接

不指定生成的文件会把编译后的文件保存到【源文件名.o】中

gcc test.i -c

gcc test.s -c -o test.o

gcc test.c -c

gcc test.c -c -o test.o

-o [file1] [file2]

[file2] -o [file1]

把文件file2编译成可执行文件file1

gcc test.c -o a.out

gcc -o a.out test.c

gcc test.o -o a.out

gcc -o a.out test.o

-I Directory 指定include包含文件的搜索目录 gcc test.c -I ./include/
-g 在编译时,生成调试信息,表示该程序可以被调试器调试
-D

程序编译时,指定一个宏,该宏内的代码会被执行;

-D后加宏的名字,如示例中【DEBUG】就是代码中宏名

gcc test.c -o a.out -DDEBUG
-w 不生成任何警告信息
-Wall 生成所有警告信息
-On 表示编译器的优化,n取值范围0~3,-O0表示没有优化,-O1默认,-O3优化级别最高
-l 程序编译时,指定使用的库
-L 指定编译的时候,搜索的库的路径

-fPIC

-fpic

生成与位置无关的代码
-shared 生成共享目标文件,通常在建立共享库时使用
-std 指定c标准,默认是GNU C -std=c99,表示使用c99标准

二、静态库的制作及使用

1. 库是什么

库文件是计算机上的一类文件,可以简单的把库文件看作是代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类。

库是特殊的一种程序,编写库的程序和编写一般的程序区别不大,只不过库不能单独运行。

库文件有两种:静态库和动态库,动态库又称为共享库,二者区别:
        静态库在程序的链接阶段被复制到程序中;
        动态库/共享库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。

库当然是有优点的,使用库可以使代码保密,并且方柏霓部署和分发。

2. 静态库的制作及使用

静态库在不同平台下文件类型不同。

window下静态库为.lib文件,libxxx.lib;

linux下静态库为.a文件,libxxx.a;

lib是前缀,表示是库文件,是固定的;
xxx是库名;
.lib/.a是后缀名,是固定的;

2.1. 制作静态库

Linux下静态库的制作过程:

  1. gcc命令获得.o文件:gcc -c xx.c xx.c xx.c
  2. 使用ar工具(archive),将.o文件打包
             ar rcs libxxx.a xxx.o xxx.o
      r - 将文件插入到备存文件中
      c - 建立备存文件
      s - 索引

示例,有如下文件夹,包含calc和library两个文件夹,calc文件夹内包含
        add.c——加法计算、div.c——除法计算、mult.c——乘法计算、sub.c——减法计算
        head.h——加、减、乘、除的函数声明
        main.c——对加、减、乘、除函数的调用

【Linux】Linux下静态库(.a)和动态库(.so)的制作和使用_第2张图片

 各个文件的内容如下:

head.h:

#ifndef _HEAD_H
#define _HEAD_H

// 加法
int add(int a, int b);
// 减法
int subtract(int a, int b);
// 乘法
int multiply(int a, int b);
// 除法
double divide(int a, int b);

#endif

add.c:

#include 
#include "head.h"

int add(int a, int b)
{
    return a+b;
}

div.c:

#include 
#include "head.h"

double divide(int a, int b)
{
    return (double)a/b;
}

mult.c:

#include 
#include "head.h"

int multiply(int a, int b)
{
    return a*b;
}

sub.c:

#include 
#include "head.h"

int subtract(int a, int b)
{
    return a-b;
}

main.c:

#include 
#include "head.h"

int main()
{
    int a = 20;
    int b = 12;
    printf("a = %d, b = %d\n", a, b);
    printf("a + b = %d\n", add(a, b));
    printf("a - b = %d\n", subtract(a, b));
    printf("a * b = %d\n", multiply(a, b));
    printf("a / b = %f\n", divide(a, b));
    return 0;
}

制作静态库的过程:

1>  进入calc文件夹

~/Linux/lesson4$ cd calc
~/Linux/lesson4/calc$ ls
add.c  div.c  head.h  main.c  mult.c  sub.c

2> 编译add.c、div.c、mult.c、sub.c文件,生成为.o文件

~/Linux/lesson4/calc$ gcc -c add.c div.c mult.c sub.c
~/Linux/lesson4/calc$ ls
add.c  add.o  div.c  div.o  head.h  main.c  mult.c  mult.o  sub.c  sub.o

3> 把生成的.o文件打包成静态库文件,假定库名为cacl,可以看到已经生成了libcacl.a库文件

~/Linux/lesson4/calc$ ar rcs libcacl.a add.o div.o mult.o sub.o
~/Linux/lesson4/calc$ ls
add.c  div.c  head.h     main.c  mult.o  sub.o
add.o  div.o  libcacl.a  mult.c  sub.c

2.2. 使用静态库

使用静态库时需要用
        -l命令指定使用的库名;
        -L命令指定库所在的路径。

如上在calc文件夹内,main.c文件生成可执行文件使用到库libcacl.a

~/Linux/lesson4/calc$ gcc main.c -o app -lcacl -L ./
~/Linux/lesson4/calc$ ls
add.c  app    div.o   libcacl.a  mult.c  sub.c
add.o  div.c  head.h  main.c     mult.o  sub.o
~/Linux/lesson4/calc$ ./app
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667

如果生成的库在library/lib文件夹内,如下。

【Linux】Linux下静态库(.a)和动态库(.so)的制作和使用_第3张图片

 在library文件夹中编译main.c生成可执行文件,那么还需要指明包含文件的路径

~/Linux/lesson4/library$ gcc main.c -o app -I ./include/ -lcacl -L ./lib/
~/Linux/lesson4/library$ ls
app  include  lib  main.c  src
~/Linux/lesson4/library$ ./app
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667

 2.3 静态库的制作和使用总结

制作静态库:
        首先使用【gcc -c xxx.c xxx.c】编译为.o文件;然后使用【ar rcs libxxx.a *.o】命令把需要的.o文件打包为静态库文件。

使用静态库:
        使用【gcc main.c -o app -I [包含的头文件路径] -L [库文件路径] -l[库名]】命令编译生成可执行文件即可。

静态库制作完成后分发给别人使用时,需要把对应的头文件和静态库文件发出去。

三、 动态库的制作和使用

动态库在不同平台下的文件类型不同

windows平台下是 libxxx.dll;
linux平台下是libxxx.so;

lib是前缀,表明是库文件,固定的;
xxx是库名;
.dll/.so是后缀,固定的

1 制作动态库

linux下制作动态库的过程:

  1. gcc得到.o文件,得到和位置无关的代码:gcc -c -fpic/fPIC xx.c xx.c
  2. gcc得到动态库,使用-shared参数:gcc -shared xx.o xx.o -o libxx.so

针对上面的library文件夹,按照如下命令生成动态库

1> 生成.o文件

/library/src$ gcc -c -fpic *.c -I ../include/

2> 生成动态库

/library/src$ gcc -shared *.o -o libcalc.so

2. 使用动态库

2.1 动态库的工作原理

首先了解下库的工作原理。

静态库是GCC进行链接时,会把静态库中代码打包到可执行程序中;
动态库是GCC进行链接时,动态库的代码不会被打包到可执行程序中;

程序启动后,动态库会被动态加载到内存中,通过ldd 命令可以检查动态库的依赖关系,如下main.c调用动态库生成的可执行文件,使用ldd命令查看依赖关系。

如下用到的libcalc.so是因为执行程序没有找到该动态库的地址,所以显示【not found】。

2.2 定位共享库

动态库使用时需要先定位到它所在的位置。当系统加载可执行代码时,能够知道其所以来的库的名字,但是还需要知道绝对路劲。就需要系统的动态载入器获取绝对路径。对于elf格式的可执行程序,是由ld-linux.so完成的,它先后搜索elf文件的DT_RPATH段——>设置环境变量LD_LIBRARY_PATH——>/etc/ld.so.cache文件列表——>/lib/usr/lib目录找到库文件后将其载入内存。

具体定位共享库的方法有如下:

  • 第一种,配置环境变量,一次性的,只有一个终端可用。

在当前终端下输入如下命令,冒号后面是共享库所在的路径。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/usr/Linux/lesson4/library/lib

方便的获取动态库的路径,先定位到动态库所在路径下,然后使用【pwd】命令可以获取当前目录的完整路径。

然后可以输入以下命令查看环境配置是否成功:

echo $LD_LIBRARY_PATH

显示如下表示配置环境变量LD_LIBRARY_PATH成功:

 这种配置只能用在当前终端,属于一次性的。

  • 第二种,用户级别的配置,只对当前用户有效。对.bashrc文件配置

首先【cd】命令进入home目录,【ll】命令查看home目录下的文件及文件夹

【Linux】Linux下静态库(.a)和动态库(.so)的制作和使用_第4张图片

然后【vim .bashrc】命令对.bashrc文件编辑,最后一行加入如下内容,保存退出。

【Linux】Linux下静态库(.a)和动态库(.so)的制作和使用_第5张图片

 最后使用【. .bashrc】或【source .bahsrc】命令更新配置。 

 使用【echo $LD_LIBRARY_PTAH】命令查看是否添加成功:

  •  第三种,系统级别的环境变量配置,需要root权限。

首先,系统级别的环境配置需编辑【/etc/profile】文件

$ sudo vim /etc/profile

在文件最后添加:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/usr/Linux/lesson4/library/lib

然后,使用命令更新编辑后的文件

$ source /etc/profile

查看是否添加成功

$ echo $LD_LIBRARY_PATH

显示添加的路径表示添加成功。

  • 第四种,修改【/etc/ld.so.cache】文件

【/etc/ld.so.cache】文件是二进制文件,不能修改,那么通过修改/etc/ld.so.conf文件,把共享库的路径添加,最后更新配置,具体命令:

检查是否添加成功,转到可执行程序目录,使用【ldd xxx】命令查看依赖库能否找到。

  • 第五种,把共享库文件放到/lib和/use/li目录下,不建议使用该种方法。

3.  动态库的制作和使用总结

制作动态库

首先使用【gcc -c -fpic xx.c xx.c】或【gcc -c -fPIC xx.c xx.c】命令编译生成.o文件,然后使用【gcc -shared xx.o xx,o -o libxx.so】命令生成共享库。

使用动态库

需要先定位到共享库,可以配置环境变量:【LD_LIBRARY_PATH】或者修改【/etc/ld.so.conf】文件,使共享库能够被定位到。

动态库分发给别人使用时需要提供头文件和动态库文件。

以上参考:课程列表_牛客网 (nowcoder.com)https://www.nowcoder.com/study/live/504/1/8

四、 静态库和动态库的比较

静态库的优点:

  • 静态库被打包到应用程序中加载速度快;
  • 发布程序无需提供静态库,移植方便

静态库的缺点:

  • 消耗系统资源,浪费内存:
  • 更新、部署、发布麻烦;

动态库的优点:

  • 可以实现进程间资源共享;
  • 更新,部署,发布简单;
  • 可以控制何时加载动态库

动态库的缺点:

  • 加载速度相对静态库慢一些;
  • 发布程序时需要提供依赖的动态库;

全文参考:

课程列表_牛客网 (nowcoder.com)

你可能感兴趣的:(Linux,linux,运维,服务器)