Linux 动态库和静态库

                        Linux 动态库和静态库_第1张图片

       今天主要介绍动静态库的制作和使用,对于库来说,大家肯定都不陌生,因为我们写代码基本都要使用库。今天我们来进一步学习库的知识吧~

目录

库的介绍

库的分类

库文件的命名

库的真实名字

查看可执行程序依赖的库

思考

静态库

制作静态库

使用静态库 

总结

动态库

制作动态库

使用动态库 

运行方法一

运行方法二 

运行方法三

总结 

补充


库的介绍

库的分类

一般库分为两种:动态库、静态库

在Linux中

如果是动态库:库文件是以.so作为后缀的!

如果是静态库:库文件是以.a作为后缀的!

在windows中

如果是动态库:库文件是以.dll作为后缀的!

如果是静态库:库文件是以.lib作为后缀的!

今天我们主要讨论Linux下的动静态库的制作和使用。

库文件的命名

库文件的命名:libXXX.so  或者    libYYYY.a-..

其实也就是lib加库名字,再加后缀,后缀后面可能还跟一些东西。

比如我们看一下系统库文件的命名:

[cyq@VM-0-7-centos test]$ ls /lib64/libc.so.6 -l

[cyq@VM-0-7-centos test]$ ls /lib64/libc-2.17.so -l

 

我们发现库文件名:libc-2.17.so 

库的真实名字

去掉lib前缀,去掉.a- 或 .so-(包含后缀)之后的部分,剩下的就是库名称。

举个栗子:

libc-2.17.so:去掉前缀lib,去掉后缀.so,剩下的c-2.17就是库的真实名字。

查看可执行程序依赖的库

[cyq@VM-0-7-centos 库]$ ldd test

我们来查看一个可执行程序依赖的动态库:

我们可以发现这个这个可执行程序依赖的是动态库(直接去编译,linux默认是动态链接)。

如果我们静态链接方式去编译再来查看这个可执行程序依赖的库:

[cyq@VM-0-7-centos 库]$ gcc -o test test.c -static
[cyq@VM-0-7-centos 库]$ file test

 

 接下来才是今天的主要内容~

思考

库本身就是二进制文件,我们如何得知,一个库给我们提供了什么方法?

一套完整的库:库文件本身、头文件、说明文档

头文件是文本形式的,会说明库中暴露出来的方法的基本使用!

我们在C/C++中,为什么有时候写代码的时候,.h里面放上声明,.c/.cpp里面放入函数的定义?为什么要这么实现?

因为我们要制作库!

       函数定义和函数实现进行分离,一是方便维护。还有一个原因是为了私密。因为有时候别人想使用我们的代码,但是我们又不能把源码发给别人,所以我们可以制作成库(库是二进制文件),然后给别人使用,这样就保证了私密性。

静态库

制作静态库

我们在此之前,先创建add.h、add.c、sub.h、sub.c四个文件,.h文件里面写函数声明,.c文件里面写函数定义。

如下:

add.h

#pragma once
#include

int my_add(int x, int y);

add.c

#include "add.h"

int my_add(int x, int y)
{
    return x + y;
}

sub.h

#pragma once
#include

int my_sub(int x, int y);

sub.c

#include "sub.h"

int my_sub(int x, int y)
{
    return x - y;
}

Makefile

libmymath.a:add.o sub.o
	ar -rc $@ $^
%.o:%.c
	gcc -c $<
.PHONY:clean
clean:
	rm -f libmymath.a *.o
.PHONY:output
output:
	mkdir lib
	cp libmymath.a lib
	cp *.h lib

实际上Makefile动作:

第一步就是把对应的.c文件生成对应的.o文件。

注意:我们不用写main函数,因为在链接之前每个文件预处理、编译、汇编(生成可重定向位的二进制文件)动作是独立的,只有到链接的时候才会相互交叉。补充:在vs环境下运行没有写main实际上报错就是链接错误~

第二步就是把生成的.o文件打包成库文件。

第三步,把库文件和.h头文件进一步打包,放在一起,就可以发布了。

第一步&第二步:

libmymath.a:add.o sub.o //我们生成libmymath.a库文件时,依赖目标是add.o和sub.o,如果在指定路径下(默认是当前路径)没有找到,就先会先找到add.o的生成方法。

    ar -rc $@ $^ //ar是gnu归档工具,rc表示(replace and create),意思就是,没有就创建,有的话就替代。这是打包静态库的工具。

%.o:%.c //当前路径下所有要生成的.o文件都要依赖%.c文件

    gcc -c $< //把每个.c文件编译生成对应的.o文件。

第三步:

.PHONY:output //伪目标,总是可以被执行

output:

    mkdir lib //创建一个目录

    cp libmymath.a lib //把打包的静态库文件拷贝到该目录下

    cp *.h lib //把当前目录下的所有.h文件拷贝到该目录下

最后:

.PHONY:clean

clean:

    rm -f libmymath.a *.o //删除库文件和当前目录下的所有.o文件

接下来我们就来看一下这个过程:

Linux 动态库和静态库_第2张图片

这样,我们就生成好了我们的静态库lib,这样我们就可以发给别人了。

使用静态库 

我们假设把制作好的lib库发送给friend_lib,然后friend_lib使用我们的库写代码时:

Linux 动态库和静态库_第3张图片

我们看到,在friend_lib目录下有我们给他的lib库,同时在friend_lib目录下写了一个test.c代码,使用了lib库。 

test.c

#include "./lib/add.h"
#include "./lib/sub.h"

int main()
{
  int x = 10;
  int y = 20;

  int r1 = my_add(x, y);
  int r2 = my_sub(x, y);

  printf("+: %d\n",r1);
  printf("-: %d\n",r2);
  return 0;
}

运行指令:

[cyq@VM-0-7-centos friend_lib]$ gcc -o test test.c -I./lib -L./lib -lmymath

[cyq@VM-0-7-centos friend_lib]$ gcc -o test test.c -I./lib -L./lib -lmymath
-I./lib:告诉编译器,编译时到lib目录下找的头文件。(如果头文件指明了路径就可以不写)

-L./lib:指定库路径。

-lmymath:告诉编译器,在指定的路径下,找哪个库,-l后面加库的真实名字。

实验如下:

Linux 动态库和静态库_第4张图片

总结

1、我们把自己写好的.c文件编译生成对应的.o文件。

2、使用归档工具ar -rc把生成的.o文件打包成静态库。

3、然后把打包的库和自己写的.h头文件放在一个目录下就可以给别人了。

我们给别人交付的其实就是一个库文件+一套头文件。

动态库

制作动态库

我们制作动态库方法和静态库类似,只是Makefile里面的细节不一样:

add.h

#pragma once
#include

int my_add(int x, int y);

sub.h

#pragma once
#include

int my_sub(int x, int y);

add.c

#include "add.h"

int my_add(int x, int y)
{
    return x + y;
}

sub.c

#include "sub.h"

int my_sub(int x, int y)
{
    return x - y;
}

Makefile

libmymath.so:add.o sub.o
	gcc -shared -o $@ $^
%.o:%.c
	gcc -fPIC -c $<
.PHONY:clean
clean:
	rm -f libmymath.so *.o
.PHONY:output
output:
	mkdir so
	cp libmymath.so so
	cp *.h so

第一步就是把所有的.c文件生成对应的.o文件,同时借助-fPIC,生成位置无关码。

第二步将生成的.o文件打包成动态库,同时借助-shared,表示生成共享库格式。

第三步把生成的动态库和.h头文件放到一个目录下,就可以交付给别人了。

第一步&第二步:

libmymath.so:add.o sub.o //生成目标文件libmymath.so依赖add.o和sub.o,如果依赖的文件找不要的话,就向下执行依赖文件的生成方法。

    gcc -shared -o $@ $^ //将生成的.o文件打包成动态库,-shared表示共享库格式

%.o:%.c //所有将生成的.o文件依赖对应的.c文件

    gcc -fPIC -c $< //生成方法,-c就是生成.o文件,$<对应上面的%.o:%.c。

第三步:

.PHONY:output

output:

    mkdir so //创建so目录

    cp libmymath.so so //把libmymath.so动态库文件拷贝到so目录中。

    cp *.h so //把当前目录下的所有.h文件拷贝到so目录中

实验效果:

Linux 动态库和静态库_第5张图片

这样生成的动态库交给别人了~

使用动态库 

如果我们把上面打包好的动态库,给friend_dll。然后再friend里面写一个调用so动态库的函数,关系如下:

Linux 动态库和静态库_第6张图片

test.c 

#include "./so/add.h"
#include "./so/sub.h"
int main()
{
  int x = 10;
  int y = 20;

  int r1 = my_add(x, y);
  int r2 = my_sub(x, y);

  printf("+: %d\n",r1);
  printf("-: %d\n",r2);
  return 0;
}

 但可惜,我们如果直接编译,是没问题,但是我们运行就不对了:

       那我可能就好奇了,我不是指明头文件路径了吗?我不是指明库路径了吗?我们也指明了是哪个库啊?为什么还是不行?

       实际上我们那是在编译的时候指明了,等到运行可执行文件的时候还是要去库,这时候编译器就不知道了,因为没有人告诉它,这时候我们需要去再多做一步,在运行时"告诉编译器"库在哪。

       静态库不存在这样的问题,因为他在编译的时候就已经把库拷贝进可执行程序里面了,运行时立马就能找到对应的库。

我们先用在上面讲过的ldd命令看看:

这时候我们就发现,我们的代码在运行时找不到我们的库!

       我们曾经使用-I和-L以及-l选项是在编译期间告诉编译器到哪里去找,这时候已经生成了.o目标文件了,但是在运行时,却没有告诉编译器去哪里找我们要使用的库,换句话说,也就是环境变量里没有对应的搜索路径! 

注意:这里说的环境变量不是PATH,PATH是保存搜索指令的路径的环境变量。这里的环境变量保存的是库文件的搜索路径。

之前的库,在系统默认路径下:/lib64、/usr/lib、/usr/include等,编译器是能够识别这些路径的。

在这里我们就知道了,安装软件其实就是把对应的库和头文件下载然后拷贝到默认目录下。

运行方法一

把我们的库路径拷贝到LD_LIBRARY_PATH环境路径下。

LD_LIBRARY_PATH

这是可以用来存放搜索库文件位置的路径的环境变量。

使用方法和PATH一致。

[cyq@VM-0-7-centos friend_dll]$ pwd
/home/cyq/practice/test/库/friend_dll
[cyq@VM-0-7-centos friend_dll]$ echo $LD_LIBRARY_PATH
:/home/cyq/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
[cyq@VM-0-7-centos friend_dll]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/cyq/practice/test/库/friend_dll/so
[cyq@VM-0-7-centos friend_dll]$ echo $LD_LIBRARY_PATH
:/home/cyq/.VimForCpp/vim/bundle/YCM.so/el7.x86_64:/home/cyq/practice/test/库/friend_dll/so

注意:路径精确到这个so动态库!! 

 这时候我们编译再ldd查看生成的可执行程序:

[cyq@VM-0-7-centos friend_dll]$ gcc -o test test.c -I./so -L./so -lmymath

我们发现test.c就可以找到对应的so库了:

这时候我们就可以运行了:

运行方法二 

可以修改系统默认的库路径搜索路径。下面两个也是博主曾经介绍过的~

vim ~/.bash_profile

vim ~/.bashrc

这样做可以永久生效,但是严重不推荐,这样做会污染人家的库文件池。

运行方法三

配置全局的搜索路径。

需要在root用户下进行配置。

我们首先进入到/etc/ld.so.conf.d/目录下:

ld.so.conf.d:系统在搜索动态库时搜索路径。

[root@VM-0-7-centos ld.so.conf.d]# cd /etc/ld.so.conf.d/

 我们看到当前目录下有很多.conf的文件,我们在这里也可以创建自己的文件,然后把对应的动态库路径保存进去:

[root@VM-0-7-centos ld.so.conf.d]# vim cyq.conf 

然后使用ldconfig让路径缓存更新一下:

[root@VM-0-7-centos ld.so.conf.d]# ldconfig

 接着我们就可以在别的终端运行我们的代码了:

总结 

如何制作库?

1、所有的源代码都要被编译成.o(可重定向目标文件)

2、将.o打包,使用ar -rc或者gcc -shared -fPIC进行打包

3、交付include和.a或者.so文件

如何使用?

具体看上面介绍。

补充

编译器动态链接优先、静态链接优先?

如果只提供静态库,我们只能将我们的库,静态链接到我们的程序中。

如果只提供动态库,我们只能将我们的库,动态链接到我们的程序中。

如果想既能动态链接,又能静态链接,这时候就应该提供两种库文件!

gcc、g++默认的是动态链接优先、release优先。

      如果我们想静态链接就首先要安装静态库,因为,一般服务器可能没有内置语言的静态库,只有动态库。

静态库安装好之后,在编译的时候,在后面加 -static选项就可以静态链接了。

看到这里,给博主点个赞吧~

                                              Linux 动态库和静态库_第7张图片

你可能感兴趣的:(debian,运维,linq,linux)