首页 > 系统相关 >linux静态库与动态库整理

linux静态库与动态库整理

时间:2023-01-30 10:45:26浏览次数:60  
标签:编译 静态 liuwh int linux libmod 动态

简化版本

静态库:

  • 制作:
gcc -g -c ./*.c -I ../include/
ar crs libmod.a *.o
  • 使用
gcc -g main.c -L ./ -lmod -I ../include/

动态库:

  • 制作:
gcc -g -c -fPIC -Wall ./*.c -I ../include/
gcc -g -fPIC -shared -o libmod.so ./*.o

or
gcc -g -fPIC -Wall ./*.c -I ../include/ -shared -o libmod.so

  • 使用
gcc -g main.c -L ./ -lmod -I ../include/

注意,要将库放入可以被系统搜索到的位置,详情见下面的动态库使用


测试代码

下面是测试使用的代码

目录结构

├── include                ## 库头文件
│   └── mod.h
├── source                 ## 库文件
│   ├── add.c
│   ├── div.c
│   ├── mul.c
│   └── sub.c
└── test                   ## 测试代码
    └── main.c

库 头文件

库是一个非常简单的例子 就是加减乘除,但是这里我们重点是制作库 而不是里面的函数。

mod.h

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

add.c

#include "mod.h"

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

sub.c

#include "mod.h"

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

mul.c

#include "mod.h"

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

div.c

#include "mod.h"

int div(int a,int b){
        return a/b;
}

test.c

#include "mod.h"
#include <stdio.h>
int main(){
        printf("hello world\n");
        printf("a + b = %d\n",add(1,3));
        printf("a - b = %d\n",sub(1,3));
        printf("a * b = %d\n",mul(1,3));
        printf("a / b = %d\n",div(4,3));
        return 0;
}

静态库

静态库简介

静态库被称为归档文件,是UNIX系统提供的第一种库。
实际上就是简单的一个普通的目标文件的集合,一般来说习惯用“.a”作为文件的后缀。
可以用ar这个程序来产生静态函数库文件。Ar是archiver的缩写。

  • 可以将一组进程被用到的目标文件整合为单独的库文件,这样就可以使用它构建程序时,无需重新编译原来的源代码。
  • 链接命令变得更为简单,只需要指定静态库名称即可。链接器知道如何搜索静态库并将可执行程序需要的对象抽取出来。

静态库函数允许程序员把程序link起来而不用重新编译代码,节省了重新编译代码的时间。不过,在今天这么快速的计算机面前,一般的程序的重新编译也花费不了多少时间,所以这个优势已经不是像它以前那么明显了。静态函数库对开发者来说还是很有用的,例如你想把自己提供的函数给别人使用,但是又想对函数的源代码进行保密,你就可以给别人提供一个静态函数库文件。理论上说,使用ELF格式的静态库函数生成的代码可以比使用共享函数库(或者动态函数库)的程序运行速度上快一些,大概1-5%

静态库优劣

  • 优点:
    1. 隐藏代码内部细节,不需要暴露源码
    2. 无需重复编译静态库源码
    3. 无需外部链接,静态库会被编译进可执行程序内部,编译程序完成后,静态库可以删除
  • 缺点:
    1. 静态库编译进可执行程序内部,可执行程序占用更多的磁盘空间
    2. 运行后的可执行程序,相同的静态库在代码段各自有一份,造成内存空间浪费
    3. 升级静态库,需要重新编译所有的可执行程序,无法模块化升级。

静态库命名规则

libname.a

  • lib: 固定名称
  • name: 库名
  • .a :固定后缀

静态库制作

静态库的制作十分容易(相比动态库),仅需要以下指令。

  1. 将源码程序编译为.o文件

cd到source目录下,执行以下语句

gcc -g -c ./*.c -I ../include/
  • -g :添加调试信息
  • -c :编译为.o文件
  • -I : 指定头文件目录
  1. 将.o文件打包为静态库
ar crs libmod.a *.o
  • ar : 静态库打包命令
  • crs:可选参数
    • c 不在必须创建库的时候给出警告
    • r 替换归档文件中已有的文件或加入新文件
    • s 创建归档索引 (cf. ranlib)

后面跟库名称,以及.o文件

静态库使用

静态库的使用也很简单,只需要编译的时候注意一下链接就好。
cd 到test目录下

gcc -g main.c -L ../static/ -lmod -I ../include/
  • L 指定库目录
  • l(小L) 指定库名
  • I(大i) 指定头文件目录

静态库注意

关于静态库需不需要头文件的问题?

不一定需要。创建一个库一般处于以下两种目的:

  1. 把一些相关的代码,打包成一个库,发布给其它的人用。
    这中情况是最常见的情况,如写 C 语言用到 libgcc。在这种情况下,你除了提供库文件:静态库[ windows 下 .lib,linux .a];动态库:[Windows 下 .dll,Linux 下 .so] 之外,必须提供头文件。头文件是你这个库里面提供了那些接口可以供外界使用。 如果没有头文件,其他人无法使用,因为不知道函数方法的原型!

  2. 在为某些软件项目写插件,而这些项目软件是公司内部的;或者说自己相对熟悉可接触的,即然是可以直接得知可能用到的函数方法的原型(函数名,参数列表,返回值等)的;就没有必要单独列出头文件,直接作为库使用也是可以的;很多大的项目,都是模块化设计,留有一些特定的接口,方便定制。当程序运行时,会动态加载指定目录下的动态库,运行时调用动态库里面约定好的方法。这种情况无需提供头文件,但要按照特定的约定来实现这个库。

总之:
当调用方还不知道不清楚函数原型的时候:动态库中的函数方法的原型(函数名,参数,返回值等)的情况下;

  • 代码编写时候

调用方是不知道如何使用该库的,所以是需要头文件帮助,来编写调用代码的;尤其是用到了头文件中声明的类;类型和相关变量,相关函数;
然后库文件存在就可以直接调用.

  • 代码编译时

如果是静态调用;无论是静态库或动态库,都是需要库的头文件参加编译的;
如果是动态加载动态库(dlopen/load等方法),则不需要头文件,只需要库文件.前提是调用方知道函数名和参数列表,返回值等信息,方可正确调用;

  • 代码运行时

运行时,无论静态库还是 动态库,都不需要头文件;

参考链接:https://blog.csdn.net/weixin_42073232/article/details/110531589

静态库相关命令

ar命令详情见man手册和help

  • 查看库内模块
liuwh@liuwh-PC ~/D/l/source> ar tv libmod.a
  • 替换库内模块
liuwh@liuwh-PC ~/D/l/source> ar r libmod.a add.o
  • 删除库内模块
liuwh@liuwh-PC ~/D/l/source> ar d libmod.a mul.o
  • 追加库内模块
liuwh@liuwh-PC ~/D/l/source> ar q libmod.a mul.o 

动态库

动态库简介

简单说,动态库是为了解决静态库的痛点出现的。

动态库的关键思想是,动态库被所有需要这些模块的程序共享,动态库不会像静态库一样被复制到链接的可执行程序中,相反,当第一个需要动态库中的模块的程序启动时(通常第一次加载动态库的程序会慢一点,后面的程序启动时不需要加载会快一些),库的副本才会被加载到内存中。

当后续进程需要该动态库中的模块时,可以直接使用已经加载到内存中的库的副本(放心,不会造成内存冲突,对于库内的变量都有各自的地址)。这样的好处是可执行程序需要的磁盘空间和虚拟内存空间减少了。

ps:为什么我会说模块呢?因为假如一个动态库有三个.o文件组成,那么只有在调用其中某个.o文件时才回去找该文件内的符号地址,不会一次性将三个模块都加载进来。(这是根据资料推测的,有待证实

标签:编译,静态,liuwh,int,linux,libmod,动态
From: https://www.cnblogs.com/zzLiuwh/p/17074760.html

相关文章

  • ArrayList动态扩容
    一、ArrayList的动态扩容机制要了解其动态扩容机制就必须先看它的源码,源码是基于jdk1.8的1.ArrayList的主要属性//如果不指定容量(空构造器),则在添加数据时的空......
  • 浅谈Linux下file的应用实例
    file的官方解释为:file - determine file type也就是说可以识别文件类型的意思,也可用来辨别一些文件的编码格式。下面看几个比较使用的例子。实例一:默认file后......
  • linux常用系统维护命令
    “top”命令:监视进程和Linux整体性能 Linux:查看占用CPU或内存最多进程:psaux|sort-k3nr|head-10 用命令组合查看CPU占用最多的前10个进程:psaux|head-1;p......
  • Linux 设置静态IP
    Linux设置静态IP修改配置文件 vim/etc/sysconfig/network-scripts/ifcfg-ens160 源文件内容如下  修改后               ......
  • ServletContext与静态变量(static)的区别,数据库连接池放在哪里
    这种是放在static中,03_用servlet、request和Druid技术写登录案例   java——数据库连接池——druid_基本使用这是放在ServletContext中,使用ServletContext缓存数据源......
  • Linux 内存地址为何是从 0x7c00 开始 All In One
    Linux内存地址为何是从0x7c00开始AllInOneassemblylanguageprogramminghttps://en.wikipedia.org/wiki/Assembly_languagehttps://zh.wikipedia.org/wiki/......
  • 实用Linux高级命令篇
    一、实用的xargs命令在平时的使用中,我认为xargs这个命令还是较为重要和方便的。我们可以通过使用这个命令,将命令输出的结果作为参数传递给另一个命令。比如说我们想......
  • linux普通用户上传文件失败
    解决方法: 给需要上传文件的目录授权,例如,你需要将本地文件上传到/opt/projects/目录下,你的普通户用户账号是opssudochown-Rops:ops/opt/projects/......
  • Linux下编写USB驱动实例
    USB是连接计算机系统与外部设备的一种串口总线标准,也是一种输入输出接口的技术规范,被广泛地应用于个人电脑和移动设备等信息通讯产品,USB就是简写,中文叫通用串行总线。我......
  • Linux 标准分区扩容
    0x00 前提安装growpart工具yuminstall-ycloud-utils-growpartyuminstallxfsprogs0x01使用growpart扩容工具扩容growpart/dev/sda1#注意磁盘和序号之间......