【Linux】动静态库

打印 上一主题 下一主题

主题 858|帖子 858|积分 2574



  
静态库

静态库是一种在编译阶段将库文件的内容直接整合到目标程序中的库文件形式。利用静态库后,库的代码会成为可实行文件的一部分,运行时不需要依赖外部库。
静态库的手动创建过程


我们写两个c文件,并且两个c文件都带有头文件:
my_stdio.c
  1. #include "my_stdio.h"
  2. #include<sys/stat.h>
  3. #include<sys/types.h>
  4. #include<fcntl.h>
  5. #include<string.h>
  6. #include<unistd.h>
  7. mFILE* mfopen(const char *filename, const char *mode)
  8. {
  9.   int fd = -1;
  10.   // 只写打开
  11.   if(strcmp(mode,"w") == 0)
  12.   {
  13.     // 只写打开文件
  14.     fd = open(filename,O_RDONLY);
  15.   }
  16.   else if(strcmp(mode,"r") == 0)
  17.   {
  18.     // 只写的情况每次打开需要清空,如果不存在需要创建
  19.     fd = open(filename,O_CREAT | O_WRONLY | O_TRUNC,0666);
  20.   }
  21.   else if(strcmp(mode,"a") == 0)
  22.   {
  23.     // 进行追加
  24.     fd = open(filename,O_CREAT | O_APPEND | O_WRONLY);
  25.   }
  26.   //如果fd还是小于0那么直接返回NULL
  27.   if(fd < 0) return NULL;
  28.   // 开辟一段空间然后返回
  29.   mFILE *mf = (mFILE*)malloc(sizeof(mFILE));
  30.   // 打开失败返回NULL
  31.   if(!mf)
  32.   {
  33.     close(fd);
  34.     return NULL;
  35.   }
  36.   mf->fileno = fd;
  37.   mf->flag =FLUSH_LINE;
  38.   mf->size = 0;
  39.   // 总大小是SIZE
  40.   mf->cap = SIZE;
  41.   return mf;
  42. }
  43. // 刷新操作
  44. void mfflush(mFILE *stream)
  45. {
  46.   if(stream->size > 0)
  47.   {
  48.     // 向对应的流中写入数据
  49.     // 刷新到内核级缓冲区
  50.     write(stream->fileno, stream->outbuffer, stream->size);
  51.     // 刷新到外设当中
  52.     fsync(stream->fileno);
  53.     stream->size = 0;
  54.   }
  55. }
  56. // 向文件中写入
  57. int mfwrite(const void *ptr,int num,mFILE *stream)
  58. {
  59.   // 1.拷贝
  60.   // 写ptr,写到stream当中,本质是写到缓冲区outbuffer当中
  61.   memcpy(stream->outbuffer+stream->size,ptr,num);
  62.   //拷贝完之后要将size扩大,更新缓冲区个数
  63.   stream->size+=num;
  64.   // 写到缓冲区当中需要刷新
  65.   // 2.检测
  66.   // 标记位是按行刷新
  67.   if(stream->flag == FLUSH_LINE && stream->size > 0&& stream->outbuffer[stream->size-1] == '\n')
  68.   {
  69.     // 刷新流
  70.     mfflush(stream);
  71.   }
  72.   // 返回写入字符串的大小
  73.   return num;
  74. }
  75. void mfclose(mFILE *stream)
  76. {
  77.   // stream中有字符直接做刷新
  78.   if(stream->size > 0)
  79.   {
  80.     mfflush(stream);
  81.   }
  82.   // 再关闭文件
  83.   close(stream->fileno);
  84. }
复制代码
my_stdio.h
  1. #pragma once
  2. #include<stdlib.h>
  3. #define SIZE 1024
  4. #define FLUSH_NONE 0// 不刷新
  5. #define FLUSH_LINE 1// 按行刷新
  6. #define FLUSH_FULL 2// 写满刷新
  7. struct IO_FILE
  8. {
  9.   int flag; // 刷新方式
  10.   int fileno; // 文件描述符
  11.   char outbuffer[SIZE]; //缓冲区
  12.   int cap; //缓冲区的容量
  13.   int size; //缓冲区的大小
  14.   //TODO
  15. };
  16. typedef struct IO_FILE mFILE;
  17. mFILE* mfopen(const char *filename, const char *mode);
  18. int mfwrite(const void *ptr,int num,mFILE *stream);
  19. void mfflush(mFILE *stream);
  20. void mfclose(mFILE *stream);
复制代码
上面两个文件是我们模拟实现的一个简单的stdio.h的库。
my_string.c
  1. #include "my_string.h"
  2. int my_strlen(const char *s)
  3. {
  4.   const char *end = s;
  5.   while(*end != '\0') end++;
  6.   return end - s;
  7. }
复制代码
my_string.h
  1. #pragma once
  2. int my_strlen(const char *s);
复制代码
另外两个文件则是实现的一个简单的string的一个接口,我们将用这四个文件来手动制作一个静态库。
方法1:
起首我们需要将两个.c文件编译为.o文件:
形成两个同名的.o文件

起首我们来了解一个命令:ar
ar命令是 Linux 下的一个归档工具,通常用于创建、修改和提取静态库(archive files)。它主要用于将多个文件(通常是目标文件 .o)组合成一个归档文件 .a,以便在编译过程中与其他代码链接利用。
常用选项:


  • c:创建归档文件。如果文件已存在,覆盖它。
  • r:将文件添加到归档中。如果已存在相同文件,更换它。
  • t:体现归档文件的内容列表。
  • x:从归档文件中提取文件。
  • d:从归档文件中删除文件。
  • q:快速添加文件到归档末端,不检查重复文件。
  • s:生成归档文件的索引(一般用于加快链接过程)。
常用语法:
  1. ar [options] archive_name file...
复制代码
我们就用这个命令来创建静态库:
我们需要用到两个选项,一个是r,一个是c,如果没有这个文件的静态库,则创建,如果有则更换。

这样就形成了一个静态库,但是这样我们还是用不了,我们还需要将库安装到系统当中:

起首我们需要将头文件拷贝到usr目录下的include当中,然后将静态库拷贝到lib64这个文件当中:

注意:这里需要用超级用户来拷贝
在做好准备之后我们可以看见,还是形成不了可实行程序:

系统还是找不到我们要用的静态库,所以我们需要指定给他说我们的静态库是什么名字:

在 gcc 中,-l 选项用于指定链接的库(library)。它的作用是告诉编译器,在链接阶段需要链接某个特定的库。l背面可以加上空格,也可以不加,我们刚刚拷贝到lib64中的是libstdio.a,但是用选项来索引库的时间名字是stdio不需要加前面的lib和后缀.a
形成可实行程序之后我们来运行一下:

方法2:不需要将静态库拷贝到lib64当中
我们先将刚刚加载到系统当中的库删除

我们先将静态库和需要编译的代码放在另一个目录下,方便实行:

可以看见,放在当前目录下的一个指定目录下是不能编译乐成的:

我们需要加上另一个选项:
在 gcc 中,-L 选项用于指定库文件的搜刮路径。它告诉编译器在指定路径中查找库文件,而不是仅利用默认的库路径(如 /lib 或 /usr/lib)。

加上L的选项还是编译不乐成,缘故原由:
缘故原由分析

  • -L 只指定了路径,但没有指定具体要链接的库

    • -L 告诉编译器在哪里查找库,但不会主动链接路径下的库。
    • 必须利用 -l<library_name> 显式指定需要链接的库。

  • 库文件的命名规则
    gcc 查找库文件时,遵循以下命名规则:

    • 静态库:文件名必须以 lib 开头,扩展名为 .a,例如 libmylib.a。
    • 动态库:文件名必须以 lib 开头,扩展名为 .so,例如 libmylib.so。
    • 当利用 -lmylib 时,编译器会在 -L 指定的路径中查找 libmylib.a 或 libmylib.so。
    如果库文件不符合上述命名规则,例如文件名是 mylib.a 或 custom_library.so,gcc 无法识别这些文件。

  • 直接利用库文件路径未显式指定路径
    如果没有通过 -l 指定库,而直接提供库文件路径,则必须利用完整路径:
    1. gcc main.c /path/to/libmylib.a -o main
    复制代码
所以我们需要加上-l选项:

第三种方法:当静态库和头文件都没有放在系统文件当中时
先将include的中的头文件删除了

将头文件和静态库分别放在这两个目录当中

然后我们编译main.c:
在利用 gcc 编译时,-I 选项用于指定头文件的搜刮路径,让编译器可以或许找到自定义或非标准路径中的头文件。

这几个分别是库的名字,和定位库,还有定位头文件。
动态库

动态库是一种在程序运行时加载的库文件,相比静态库,它可以实现代码共享更小的程序体积

动态库的特点


  • 文件扩展名

    • 在 Linux 系统中,动态库通常以 .so 为扩展名(Shared Object),例如:libmylib.so。

  • 动态加载

    • 程序运行时才会加载动态库,大幅镌汰可实行文件的大小。

  • 共享性

    • 同一动态库可以被多个程序同时利用,节省内存资源。

  • 版本更新方便

    • 更新库文件后,无需重新编译程序,只需确保接口兼容即可。


创建动态库

我们先来写一个简单Makfile
  1. libmystdio.so:my_stdio.o my_string.o
  2.         gcc -o $@ $^ -shared
  3. %.o:%.c
  4.         gcc -fPIC -c $<
  5. .PHONY:clean
  6. clean:
  7.         @rm -rf *.so *.o stdc*
  8.         @echo "clean ... done"
  9. .PHONY:output
  10. output:
  11.         @mkdir -p stdc/include
  12.         @mkdir -p stdc/lib
  13.         @cp -f *.h stdc/include
  14.         @cp -f *.so stdc/lib
  15.         @tar -czf stdc.tgz stdc
  16.         @echo "output stdc ... done"
复制代码

  1. gcc -fPIC -c $<
复制代码

这个选项中的-fPIC是形成与位置无关码


怎样利用动态库

第一种方法:拷贝到系统文件当中

第一种方法和静态库的一样,这里就不做赘述。
第二种方法:创建软链接
可以看见,固然形成了可实行程序,但是运行的时间还是找不到对应的库


固然告诉系统库在哪了,但是链接的时间还是没找到。
可以看见在lib64下创建软链接系统就可以找到我们库的位置了

第三种方法:通过控制环境变量来控制系统查找的规则
LD_LIBRARY_PATH 是 Linux 系统中用于指定动态库搜刮路径的环境变量。在运行时,动态链接器会根据此变量的值查找所需的共享库文件(.so 文件)。

  • 动态库的运行时搜刮路径

    • 默认情况下,动态链接器会在以下路径中查找共享库:

      • /lib
      • /usr/lib
      • /usr/local/lib

    • 如果动态库存放在非默认路径,需要通过 LD_LIBRARY_PATH 指定额外的搜刮路径。

  • 临时加载动态库

    • 在不修改系统配置文件(如 /etc/ld.so.conf)的情况下,为某些程序临时指定动态库路径。


当修改完环境变量之后,ldd我们的可实行程序,可以看见就可以检察到动态库了。

动态库和静态库的优先级

在 Linux 系统中,利用 gcc 或雷同工具进行编译和链接时,动态库(shared library, .so)静态库(static library, .a) 的优先级由动态链接器和链接器的搜刮顺序决定。

动态库 vs 静态库的优先级



  • 默认情况下,链接器优先选择 动态库
  • 如果没有找到对应的动态库,链接器才会选择 静态库
这种行为的主要缘故原由是:

  • 动态库可以镌汰可实行文件的大小,并支持运行时共享。
  • 静态库将整个库文件嵌入可实行文件中,增长了文件体积,且无法享受动态链接的更新优势。

库搜刮顺序


  • 动态库优先搜刮路径

    • 在默认路径中(如 /lib, /usr/lib, /usr/local/lib),动态库 .so 文件会被优先查找。
    • 如果通过 -L 指定了自定义路径,也会优先查找 .so。

  • 静态库备选

    • 如果动态库不存在,大概编译时显式指定了利用静态库的选项,链接器会尝试查找 .a 文件。


总结

本文具体先容了静态库与动态库的概念、创建方法及其利用方式。静态库通过将代码直接打包到可实行文件中,提供了程序独立性;而动态库则通过共享库文件实现了代码复用,减小了程序体积。在实际开辟中,理解动态库和静态库的优缺点,合理选择库的类型尤为紧张。
此外,文章还探究了动态库和静态库的优先级以及库的搜刮顺序,通过控制编译器选项(如 -L 和 -I)以及环境变量(如 LD_LIBRARY_PATH)来管理库的利用路径。把握这些基础知识,将帮助开辟者更灵活地处理程序链接和依赖题目,提升项目开辟服从与可维护性。
总结一句话:纯熟把握静态库和动态库的原理与实践方法,是成为良好开辟者的必经之路。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

我爱普洱茶

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表