TechBlog
首页分类标签搜索关于

© 2025 TechBlog. All rights reserved.

Linux系统动静态库的制作方法与使用技巧

12/22/2025
未分类#静态库#单片机#动态库#Stm#Linux

微信小程序星海飞驰

【Linux系统】动静态库的制作方法与使用技巧

1. 库

库是已经编写好的、成熟的、可复用的代码。在实际开发中,每个程序通常依赖大量的基础底层库,不可能每次都从零开始编写代码,因此库的存在具有重要意义。

从本质上讲,库是一种可执行代码的二进制形式,能够被操作系统加载到内存中执行。库可分为两种类型:

  • 静态库:.a(Linux)、.lib(Windows)
  • 动态库:.so(Linux)、.dll(Windows)

静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再需要静态库。

动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。

静态库的优点以及缺点:

  1. 优点:
    • 独立性强:在编译时将库文件直接链接到程序中,程序运行时无需依赖外部库文件,避免了库文件缺失或版本不一致的问题。
    • 性能较好:静态链接的程序无需在运行时进行动态加载,启动速度较快,运行时的性能也略有优势。
    • 部署简单:由于库已经被静态链接到可执行文件中,程序在部署时不需要担心库文件丢失,通常只需要一个可执行文件。
  2. 缺点:
    • 文件体积大:由于所有的库代码都被直接编译进程序中,最终生成的可执行文件通常会很大。
    • 如果多个程序使用相同的静态库,它们各自会有一份独立的库副本,可能导致内存浪费。
    • 无法更新:一旦静态库链接到程序中,如果库本身存在漏洞或需要升级,必须重新编译整个程序。无法单独更新库的版本。

动态库的优点以及缺点:

  1. 优点:
    • -节省磁盘空间:多个程序可以共享同一份动态库,避免了每个程序都包含一份副本,节省磁盘空间。
    • 节省内存:多个进程可以共享同一个动态库的内存映像,因此减少了内存的使用。
    • 更新方便:动态库可以独立于程序进行更新,只要程序能够找到新的库版本,无需重新编译程序。这使得库的维护和升级更加便捷。
  2. 缺点:
    • 启动性能较差:动态库在程序启动时需要加载,可能会增加程序的启动时间。
    • 依赖问题:程序运行时依赖外部库文件,若库文件缺失或版本不匹配,可能导致程序无法正常运行。这种情况通常称为“依赖地狱”。
    • 版本兼容性问题:在更新动态库时,如果库的接口发生变化,可能导致依赖该库的程序出现兼容性问题,需要重新编译程序。

ubuntu动静态库:

ls -l /lib/x86_64-linux-gnu/libc-2.31.so # 动态库
ls -l /lib/x86_64-linux-gnu/libc.a # 静态库

在这里插入图片描述

2. 库的制作

静态库的制作

制作静态库时,准备mystring.h、 mystring.c、mystdio.h、mystdio.c和main.c文件。

main.c

#include "mystdio.h" ""代表是自定义头文件
#include "mystring.h"
#include <string.h>
#include <unistd.h>

int main()
{
    MyFile *filep = MyFopen("./log.txt", "a");
    if(!filep)
    {
        printf("fopen error!\n");
        return 1;
    }

    int cnt = 10;
    while(cnt--)
    {
        char *msg = (char*)"hello myfile!!!";
        MyFwrite(filep, msg, strlen(msg));
        MyFFlush(filep);
        printf("buffer: %s\n", filep->outbuffer);
        sleep(1);
    }
    MyFclose(filep); 

    const char *str = "hello bit!\n";
    printf("strlen: %d\n",my_strlen(str));
    return 0;
}

mystring.h和mystring.c

// mystring.h
#pragma once
int my_strlen(const char *s);

// mystring.c
#include "mystring.h"
 
int my_strlen(const char *s)
{
    const char *end = s;
    while(*end != '\0') end++;
    return end - s;
}

mystdio.h

#pragma once     

#define MAX 1024
#define NONE_FLUSH (1<<0)
#define LINE_FLUSH (1<<1)
#define FULL_FLUSH (1<<2)

typedef struct IO_FILE
{
    int fileno;     //文件描述符
    int flag;
    char outbuffer[MAX];    //语言层面缓冲区
    int bufferlen;          //缓冲区长度
    int flush_method;       //刷新模式

}MyFile;

MyFile* MyFopen(const char* path, const char* mode);
void MyFclose(MyFile *);
int MyFwrite(MyFile *, void* str, int len);
void MyFFlush(MyFile *);

mystdio.c

#include "mystdio.h"
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

static MyFile* BuyFile(int fd, int flag)
{
    MyFile* f = (MyFile*)malloc(sizeof(MyFile));
    if(f == NULL) return NULL;
    f->bufferlen = 0;
    f->fileno = fd;
    f->flag = flag;
    f->flush_method = LINE_FLUSH;
    memset(f->outbuffer, 0, sizeof(f->outbuffer));
    return f;
}


MyFile* MyFopen(const char* path, const char* mode)
{
    int fd = -1;
    int flag = 0;
    if(strcmp(mode, "w") == 0)
    {
        flag = O_CREAT | O_WRONLY | O_TRUNC;
        fd = open(path, flag, 0666);
    }
    else if(strcmp(mode, "a") == 0)
    {
        flag = O_CREAT | O_WRONLY | O_APPEND;
        fd = open(path, flag, 0666);
    }
    else if(strcmp(mode, "r") == 0)
    {
        flag = flag = O_RDONLY;
        fd = open(path, flag);
    }

    if(fd < 0) return NULL;
    return BuyFile(fd, flag);
    
}

void MyFclose(MyFile* file)
{
    if(file->fileno < 0) return;
    //关闭文件时将缓冲区刷新
    MyFFlush(file);
    //系统调用关闭缓冲区
    close(file->fileno);
    free(file);
}

int MyFwrite(MyFile* file, void* str, int len)
{
    memcpy(file->outbuffer + file->bufferlen, str, len);
    file->bufferlen += len;
    if((file->flush_method & LINE_FLUSH) && file->outbuffer[file->bufferlen - 1] == '\n')
    {
        MyFFlush(file);
    }
    return 0;
}

void MyFFlush(MyFile* file)
{
    if(file->bufferlen <= 0) return;
    //系统调用写入到内核缓冲区,让操作系统自行刷新
    int n = write(file->fileno, file->outbuffer, file->bufferlen);
    (void)n;
    //系统调用刷新内核缓冲区
    fsync(file->fileno);
    file->bufferlen = 0;
}

使用gcc命令编译以上文件形成同名的.o文件:

gcc -c mystdio.c
gcc -c mystring.c

![[Pasted image 20250316201912.png]]

接着再使用归档命令ar来将这些.o文件打包形成库文件,其中-r选项表示将目标文件替换到库文件中,-c选项表示库文件不存在时,创建一个新的库文件。

ar -rc libmyc.a *.o

![[Pasted image 20250316202739.png]]

值得注意的是,库的命名是有要求的,需要加上前缀lib和后缀.a,所以我们上面形成的库文件的名字是myc,使用该库时直接引用的名字也应该去掉前缀和后缀。

接着我们使用gcc 命令将main.c编译链接形成可执行程序,但是需要加上-L选项来指明库文件的路径,-l选项指明链接库的名字。

gcc -o main main.c -L. -lmyc # 在当前路径寻找myc静态库

![[Pasted image 20250316204902.png]]

上面的示例是将静态库安装在当前目录下的,接着我们将静态库安装在指定目录。

执行以下指令,在当前目录下创建一个存放库文件和头文件的新目录:

mkdir -p stdc/include
mkdir -p stdc/lib
cp -f *.h stdc/include
cp -f *.a stdc/lib

![[Pasted image 20250316211347.png]]

然后我们再使用gcc进行编译,-I选项指定头文件的搜索路径,-L选项指定库文件的搜索路径,-l选项指定链接具体的哪个库。

gcc -o main main.c -I ./stdc/include/ -L ./stdc/lib/ -lmyc

![[Pasted image 20250316211347.png]]

除了上面的两种方法,还可以将我们的库安装在系统的目录下,这样上面的gcc编译指令的使用就可以简化:

sudo cp *.h /usr/include/ # 将自定义头文件拷贝到系统目录
sudo cp libmyd.a /lib64/ # 将自定义库拷贝到系统目录

将自定义库安装在系统目录下的做法极其不推荐,这样会将系统的库污染,而且使用的时候还是需要加上-l选项指定链接库的名字(我们的库是一个第三方的库)。

动态库的制作

制作动态库依然使用之前的文件作为示例。

使用gcc命令将.c编译生成.o文件时需要加上-fPIC选项,-fPIC选项时位置无关码(position independent code):

gcc -fPIC -c mystdio.c
gcc -fPIC -c mystring.c

将.o文件打包形成动态库不需要别的命令,使用gcc即可,需要加-shared选项。

gcc -o libmyc.so *.o -shared

![[Pasted image 20250316214958.png]]

使用file命令可以查看文件的属性,可以看到我们制作的库确实是动态库(shared object)

在当前目录下使用动态库:

gcc -o main main.c -L. -lmyc

![[Pasted image 20250316220608.png]]

但是执行main时却发现以下报错,原因在于动态库的链接只有在程序执行起来时才会真正的去链接,系统是不知道自定义动态库系统的路径,而静态库没有这样的问题是因为在编译时已经将静态库拷贝到可执行文件里。

![[Pasted image 20250316220646.png]]
![[Pasted image 20250316221842.png]]

在Linux中,有以下的方法来使用第三方动态库:

  1. 将库安装到系统路径
sudo cp libmyc.so /lib/

这个方法依然不推荐,因为会污染系统库。

  1. 建立对/lib/的软连接
sudo ln -s /home/Evan/LinuxCode/linux/libso/libmyc.so /lib/libmyc.so

  1. 添加LD_LIBRARY_PATH环境变量

LD_LIBRARY_PATH是程序运行时动态查找库时所要搜索的路径,只需将动态库所在的目录路径添加到LD_LIBRARY_PATH环境变量中,系统就知道自定义动态库所在路径。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/Evan/LinuxCode/linux/libso

微信小程序星海飞驰