【Linux】-再谈动静态库-手把手教你自己制作一个动静态库

【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第1张图片
作者:小树苗渴望变成参天大树
作者宣言:认真写好每一篇博客
作者gitee:gitee✨
作者专栏:C语言,数据结构初阶,Linux,C++ 动态规划算法
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!

文章目录

  • 前言
  • 一、再谈静态库
    • (1)站在库的制作者角度
    • (2)站在库的使用者角度
  • 二、再谈动态库
    • (1)站在库的制作者角度
    • (2)站在库的使用者角度
  • 三、总结


前言

我们上篇讲解完文件系统和软硬件链接,今天我们在此来谈一个熟悉的话题–动静态库,我们之前也说过这个话题,但是讲解了一个网吧的例子,让大家看到我们的动静态库的区别,以及程序是怎么去进行动静态库的调用,就相当于我们还停留在了解,今天博主带大家写一个动静态库,然后写程序使用我们自己的动静态库去操作,这样大家就可以更加的理解动静态库了。话不多说,我们开始进入正文的讲解。


讲解思路:

  1. 站在库的制作者角度
  2. 站在库的使用者角度
  3. 动态库是怎么被加载的

一、再谈静态库

(1)站在库的制作者角度

我们在之前说过,我们的库是函数的定义,还需要一个.h文件,他是库的声明,因为我们已经简单的使用了多文件开发,显然两个源文件是编译不起来的,所以必须要有头文件。静态库打包后的后缀是.a
我们需要写一个源文件定义,还有一个头文件进行声明:

mymath.h

#include

extern int myerror;

int add(int,int);
int sub(int,int);
int mul(int,int);
int div(int,int);

mymath.c

#include "mymath.h"

int myerror=0;
int add(int a,int b)
{
    return a+b;
}
int sub(int a,int b)
{
    return a-b;
}
int mul(int a,int b)
{
    return a*b;
}
int div(int a,int b)
{
    if(b==0)//出现错误讲错误码设置为1
    {
        myerror=1;
        return -1;
    }
    return a+b;
}

main.c

#include "mymath.h"

int main()
{
    printf("1+1=%d",add(1,1));//调用一个这个头文件里面的函数,就好比我们printf是stdio.h里面的函数。
    return 0;
}

我们可以直接把我们的库文件(mymath.c)和头文件(mymath.h)直接给用户,让main.c去使用,这就和我们平时进行多文件开发一下的。但是我们今天使用的是静态库链接,我们是看不到库文件里面的具体实现的,所以我们需要将这个库文件进行打包就行了。

我们包含头文件的时候,将程序进行编译链接在形成可执行程序,所以我们自己的源文件(main.c)和库文件在链接的时候才会发生,此时前面两个文件都经过了编译,都变成了.o文件,只需要把.o文件打包给源文件(main.c)就行了。
【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第2张图片
makefile:

lib=libmymath.a  //静态库文件一般都是.a的后缀

$(lib):mymath.o
	ar -rc $@ $^ //通过这个命令打包成.a文件
mymath.o:mymath.c  //先编译成.o文件
	gcc -c $^

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

.PHONY:output
output://创建一个目录将属于静态库的内容放在一个目录里面,别人就可以通过这个lib文件,就可以把所有的静态库相关的文件都下载下来
	mkdir -p lib/include
	mkdir -p lib/mymathlib
	cp *.h lib/include
	cp *.a lib/mymathlib

【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第3张图片
此时我们就自己制作了一个静态库。

(2)站在库的使用者角度

我们怎么去使用这个静态库呢??我们虽然生成了静态库,但是我们调用自己写的静态库,使用gcc编译的时候会报错
在这里插入图片描述

造成错误的原因是因为,我们的gcc是编译器,会去系统默认的路径下找对应的库文件和头文件,我们自己写的gcc并不知道,所以我们需要手动告诉gcc库文件和头文件在哪,所以需要使用下面的指令
gcc main.c -I ./lib/include -L ./lib/mymathlib -I:是找到对应的头文件,-L:是找到对应的库文件
在这里插入图片描述
【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第4张图片

居然还不可以,原因是我们找到了对应的库文件所在的路径,但是必须要指定的链接的是哪个文件,即使里面只有一个静态库,也需要指定,所有需要在加一个选项 -lmymath 静态库的名称是去前缀和后缀,才是他真正的名字。选项和名字直接最好不要加空格。
【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第5张图片


再来看一个问题
我们查看可执行程序可以使用使用ldd/file查看是什么链接方式
在这里插入图片描述
我们没有看到mymath这个静态库啊,原因是我们默认的使用的是动态链接方式,但是你没有动态库,有静态库的文件,gcc不是阻挡编译,没有动态库,就相当于使用动态链接,用的是静态库文件,因为使用什么链接方式就展示什么类型的库文件,所以这里面看不到我们的静态类型的库文件,也希望大家遇到这个问题不要困惑。


我们发现这么一串编译太不好了,有两种方式解决这个问题

  1. 放到系统默认路径下
    系统默认的头文件在 /usr/include
    在这里插入图片描述
    系统默认的库文件在/usr/lib64
    在这里插入图片描述

我们可以把自己制作的头文件和库文件放在这些默认的搜索路径下:
【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第6张图片
我们发现-l还是要加上的。

  1. 软连接的方式
    其实我们不推荐将自己制作的库放到默认的系统搜索路径下,万一对原路径下的库文件造成污染就麻烦了,所以我们不需要将自己的库文件放到默认搜索路径下,使用软连接
    来看操作:
sudo ln -s /home/xdh/gitcode/linux/动静态库/lib/include /usr/include/myinc
sudo ln -s /home/xdh/gitcode/linux/动静态库/lib/mymathlib/libmymath.a /usr/lib64/libmymath.a
//大家一定要看好路径,如果按照我这个创建的路径,就可以直接使用这个指令了

【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第7张图片

我们看到使用软连接也可以将我们的问题解决掉,但是也需要加-l,大家应该会发现我们将路径复制到默认路径下,实际就是在安装的过程,我们把对应的文件下载下来,为什么要安装,就是放在默认的搜索路径下,方便去执行。


结论就是第三方库在使用gcc的时候必须加上-l选项。第一方和第二方可以理解为系统和语言

静态库我们就讲解完毕了,我们来看动态库

二、再谈动态库

按照老思路去讲解

(1)站在库的制作者角度

博主将多写几个库文件,一起打包,让大家也看看。我峨嵋你动态库打包后的默认后缀是.so
还是需要写一个头文件和两个库文件
myprint.h

#pragma once 

#include

void print();

myprint.c

#include "myprint.h"

void print()
{
    printf("hello dy\n");
    printf("hello dy\n");
    printf("hello dy\n");
    printf("hello dy\n");
}

mylog.h

#pragma once 

#include

void Log(const char*);

mylog.c

#include "mylog.h"

void Log(const char* str)
{
    printf("warning:%s\n",str);
}

我们已经有了两个库文件,那我们应该怎么什么对应的动态库文件呢?首先动态库也是和静态库一样在链接的时候才会和原文件产生关系,所以我们将我们自己写的两个库文件需要先编译生成对应的.o文件,在进行打包成动态库文件。

肯定是有点区别的
gcc -fPIC -c mylog.c myprint.c
【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第8张图片
生成.o文件,难道是和静态库一样使用ar就可以了吗,答案肯定不是的,我们可以直接只有下面的指令去操作
gcc -shared -o libmymethod.so mylog.o myprint.o
【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第9张图片


重点:

  1. 我们发现动态库可以直接使用gcc进行打包,而静态库不行,我们可以理解为动态库是gcc的亲儿子,而静态库是干儿子,我们的gcc已经内置编码可以直接打包成动态库,这也可以简单理解为我们的链接方式默认就是动态库,但是用户最大,使用- static的时候还是静态链接,使用静态库,只有静态库的时候也是使用静态库。
  2. 我们发现.so是可执行文件,而.a文件是不可执行文件,这是因为,我们动态库是所有程序共享的,但我们自己写的源文件编译好,准备去执行的时候,还需要将动态库文件一起加载到内存进行执行,而静态库在形成可执行程序之前,就讲自己对应的代码复制到源文件里面了,执行的时候,那源文件加载内存就行了,和静态库文件就没有关系了
  3. 我们的打包成动态库文件去掉- shared,是不是就是我们一开始学的多个文件生成可执行程序,可我不想生成可执行文件,所有加了一个- shared,说这个的目的就是让大家更好的理解。

上面先不给大家演示效果,我先将下面的操作讲解完毕,在演示,我想将动静态库一起通过一个makefile同时编译,相信在进程程序替换的时候,已经说过怎么同时生成量恶搞可执行程序,默认值生成先出现的那一个,所以我们要设置为目标,接下来我们来看具体操作

makefile:

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

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


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


$(dy-lib):mylog.o myprint.o
        gcc -shared -o $@ $^
mylog.o:mylog.c
        gcc -fPIC -c $^
myprint.o:myprint.c
        gcc -fPIC -c $^

.PHONY:clean
clean:
        rm -rf *.o *.a *.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

我们使用这个makefile先来测试一下之前静态库的代码,看看我们写的对不对,博主就直接在gcc下指定具体路径了去测试了。

重点:不管是那种类型的库文件,前缀都一定要是lib,不让就会出现问题

【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第10张图片
结果也同时可以使用自己制作的静态库,但是这个结果我们发现不对,错误码应该是1,这个和我们测试没有关系,就是printf自己的特性,他是从右往左进行赋值的,在没有调用函数的时候,错误码就已经被赋值了,提前算好就行了。
【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第11张图片
这样的问题就解决了。

(2)站在库的使用者角度

我们通过上面方法,制作出属于我们自己的动态库,现在要来看看怎么去使用它。
我们使用make一键生成动态库,看效果
【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第12张图片

mylib里面就是包含我们自己制作的动静库文件,大家可以选择将两者分开,这里博主就没有分开了,因为不影响演示,接下来我们就来测试。

我们来写一个程序去使用我们的动态库
mytest.c

#include"mylog.h"
#include"myprint.h"
int main()
{
    print();
    Log("hello log function");
    return 0;
}

gcc mytest -o mytest
在这里插入图片描述
显然我们直接编译成可执行程序是不行的。这个问题不用多说,找不到路径,我们使用下面的命令,指定路径试试看
gcc mytest.c -I ../mylib/include -L ../mylib/lib -lmymethod -o mytest
【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第13张图片
我们看到可以形成可执行文件了,但是我们执行的时候去还是报错,这是为什么??
我都已经把指定路径告诉你了,让你去这个路径下面去查找,你怎么还是不行呢??
首先我们要搞懂一个问题,这个告诉你,你指的是谁(gcc)gcc只管编译,形成可执行程序,大家还记得我最上面说的重点吗,但可执行程序开始运行的时候,动态库也是要加载到内存的,刚才指定路径值告诉了gcc,没有告诉我系统你的动态库在哪里,所以才会导致我们可以形成可知心个程序,去执行不了,既然找到问题的答案了,那我们就解决这个问题,让系统知道就好了。
为什么静态库没有这个问题呢,原因就是我们在使用静态库链接的时候,代码是拷贝到可执行代码中的,形成可执行程序之后,加载到内存就和静态库没有关系了。只要编译时能找到就行了。

解决问题:
我们有四种结果办法,博主都会一一介绍的。

  1. 将动态库放到默认系统默认路径下
sudo cp ./mylib/include/myprint.h /usr/include/myprint.h
sudo cp ./mylib/include/mylog.h /usr/include/mylog.h
sudo cp ./mylib/lib/libmymethod.so /usr/lib64/libmymethod.so

【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第14张图片

  1. 使用软链接的方式
sudo ln -s /home/xdh/gitcode/linux/动静态库/mylib/lib/libmymethod.so /usr/lib64/libmymethod.so

【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第15张图片

我直接将动态库和系统默认路径进行软链接,我程序都没有重新生成,结果就可以直接跑了。大家应该可以理解这个道理。这也侧面体现出来软链接的作用以及重要性

  1. 使用环境变量去操作
    我们为什么可以这么自然而然的使用我们的系统默认路径,原因是我们有环境变量,那我们把我们对应的动态库文件放到环境变量里面就可以了
 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/xdh/gitcode/linux/动静态库/mylib/lib

【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第16张图片

所以环境变量还是非常重要的

  1. 添加到系统的配置文件里面
    第三种在下次打开shell的时候,就又不行了,所以放到配置文件里面就可以了
    在这里插入图片描述
    在这里插入图片描述
    我们自己添加一个配置文件,把我们对应的动态库文件的路径放进去

【Linux】-再谈动静态库-手把手教你自己制作一个动静态库_第17张图片

我们在使用配置文件的时候一定要注意权限,还有这个配置文件要更新一下才可以使用,下次在重启的时候,还是可以对应的动态库的。

解决疑惑:

  1. 我们发现我们把对应的库文件放系统知道就行了,为什么头文件不需要让系统知道呢?原因是我们在编译的时候就标识过了我们自己写的可执行程序是在.o文件的时候进行链接的,而头文件在编译的时候就展开到库文件里面了然后形成了.o文件,所以不需要.
  2. 那这么多方法我们使用哪一个呢??答案是最多还是第一种,虽然第一种不建议,为什么不建议,我在一开始就标注了,我们自己写的不建议,但是我们以后几乎使用的都是别人成熟的库,所以不用担心对自己系统有啥影响,所以可以直接放到系统默认的搜索路径下,所有就有了安装。

三、总结

对于动静态库大家应该都不陌生了,我们以后去公司有很大可能使用公司自己写的库去开发,这时候就需要我们自己去将公司的库配置到自己的电脑,这也是你刚开始入职的时候,要频繁的安装软甲的原因,所以大家了解原理了,到时候也不用担心了,也希望读者可以学到更多知识,我们下篇再见。

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