深入了解linux系统—— 库的制作和使用

什么是库?

库,简单来说就是现有的,成熟的代码;

就比如我们使用的C语言标准库,我们经常使用输入scanf和输出printf,都是库里面给我们实现好的,我们可以直接进行服用。

库呢又分为静态库和动态库,在Linux中静态库文件后缀.a,动态库文件后缀.so;在Windows中静态库文件后缀.lib,动态库文件后缀.dll

这里注意一下库的命名规则:

库的命名都是以lib开头,.a/.so为后缀;去掉前缀lib和后缀.a/.so剩下的部分才是库的名字;

例如C标准库libc.so,去掉前缀和后缀,c就是库的名字。

动静态链接

在之前我们知道gcc/g++在编译时默认使用动态链接,若想要进行静态链接就要带-static选项;

  • 静态链接,本质上就是程序在编译链接时,将静态库的内容链接到可执行文件中,这样可执行程序在执行时就不会再依赖库;但是静态链接的可执行文件都比较大。
  • 动态链接:本质上就是程序在编译链接时,在可执行文件和动态库之间建立某种关联,这样可执行程序在执行时机会依赖动态库。

静态库

库分为静态库和动态库,那什么是静态库呢?

静态库简单来说就是所有.o文件的归档文件,也就是说静态库就是将所有的.o文件合并在一起。

这样在链接形成可执行程序时,将静态库和.o文件再合并在一起形成可执行文件。

静态库的制作

这里提供两份源文件代码mystdio.cmystring.c来制作库

c 复制代码
//mystdio.c
#include "mystdio.h"
MYFILE* BuyFile(int fd, int flag)
{
    MYFILE* myfile = (MYFILE*)malloc(sizeof(MYFILE));
    myfile->fileno = fd;
    myfile->flag = flag;
    myfile->bufflen = 0;
    myfile->flush_buff = LINE_FLUSH;
    //初始化缓冲区
    memset(myfile->outbuff, 0, sizeof(myfile->outbuff));
    return myfile;
}
MYFILE* MyOpen(const char* pathname, const char* mode)
{
    //确定文件的打开方式
    int fd = -1;
    int flag = 0;
    if(strcmp(mode, "w") == 0)
    {
        flag = O_CREAT | O_WRONLY | O_TRUNC;
        fd = open(pathname, flag, 0666);
    }
    else if(strcmp(mode, "a") == 0)
    {
        flag = O_CREAT | O_WRONLY | O_APPEND;
        fd = open(pathname, flag, 0666);
    }
    else if(strcmp(mode, "r"))                                                                                                                                                      
    {
        flag = O_RDONLY;
        fd = open(pathname, flag);
    }
    else{
        //???
    }
    if(fd < 0) return NULL;//打开文件失败
    return BuyFile(fd,flag);
}
void MyClose(MYFILE* file)
{
    if(file == NULL) return;
    if(file->fileno < 0) return;
    MyFlush(file);
    close(file->fileno);
    free(file);
}
int MyWrite(MYFILE* file, void* str, int len)
{
    //将数据拷贝到缓冲区当中
    //int n = 0;
    if(file->bufflen + len > MAX)
    {
        //n = MAX - file->bufflen;                                                                                                                                                  
        //memcpy(file->outbuff + file->bufflen, str, n);
        //MyFlush(file);
        MyFlush(file);
    }
    //memcpy(file->outbuff + file->bufflen,(void*)((char*)str + n), strlen((char*)str) - n);
    memcpy(file->outbuff + file->bufflen,str,len);
    file->bufflen += len;
    if(file->flush_buff & NONE_FLUSH)
        MyFlush(file);
    else if((file->flush_buff & LINE_FLUSH) && (file->outbuff[file->bufflen-1] == '\n' || file->bufflen == MAX))
        MyFlush(file);
    else if((file->flush_buff & FULL_FLUSH) && (file->bufflen == MAX))
        MyFlush(file);
    return 0;
}
void MyFlush(MYFILE* file)
{
    if(file->bufflen <= 0)  return;
    write(file->fileno, file->outbuff, file->bufflen);
    file->bufflen = 0;
    fsync(file->fileno);
}

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

头文件mystdio.hmystring.h

c 复制代码
//mystdio.h
#include <sys/stat.h>    
#include <fcntl.h>    
#include <string.h>    
#include <stdlib.h>    
#include <unistd.h>    
#define MAX 10//缓冲区大小                                           
#define NONE_FLUSH  001 //0001    
#define LINE_FLUSH  002 //0010    
#define FULL_FLUSH  004 //0100
typedef struct IO_FILE{    
    int fileno;//文件描述符    
    int flag;    
    char outbuff[MAX]; //缓冲区    
    int bufflen; //缓冲区内容长度    
    int flush_buff;    
}MYFILE;
MYFILE* MyOpen(const char* pathname, const char* mode);    
void MyClose(MYFILE* file);    
int MyWrite(MYFILE* file, void* str, int len);    
void MyFlush(MYFILE* file); 
//mystring.h
int my_strlen(const char* str);   

静态库是如何生成的呢?

静态库是.o文件的归档文件,所以我们在制作库时,就要现将所有的.c文件编译形成.o文件

有了.o文件,现在就要对这些.o文件进行归档形成静态库;

这里就要使用指令ar -rc(其中argnu归档工具,-rc表示replacecreate

静态库的使用

了解了静态库是如何制作的,那我们如何去使用静态库呢?

站在一个库的使用者的角度,我们拿到一个库时,我们并不知道这个库里都实现了哪些方法;我们就要参考所有的头文件。

所以我们就可以把头文件看做库的使用手册,在头文件中记录了库中实现的方法。

现在我们获得了静态库libmyc.a和头文件mystdio.hmystring.h

我们通过查看头文件,知道了库libmyc.a实现了哪些方法,实现了test.c中使用了libmyc.a中的方法

c 复制代码
  #include "mystdio.h"    
  #include "mystring.h"    
  int main()    
  {    
      MYFILE* myfile = MyOpen("log.txt","w");    
      if(myfile == NULL) return -1;    
      const char* str = "abc-abc\n";    
      MyWrite(myfile, (void*)str, my_strlen(str));    
      MyWrite(myfile, (void*)str, my_strlen(str));
      MyClose(myfile);    
      printf("%d\n",my_strlen(str));    
      return 0;    
  } 

这里我们直接编译test.c

可以看到,存在链接时报错,找不到这些方法;这是因为gcc默认情况下只会去链接C标准库,如果想要去链接第三方库,就要带-l选项指明要链接的库。

bash 复制代码
gcc test.c -l库名

但是我们可以看到,-lmyc指明了要链接哪一个库,却找不到这个库;

这是因为gcc只会在指定路径下去寻找库,而我们要链接的库myc在当前路径下,并不在系统的指定路径下;

所以我们要使用gcc-L选项来指明我们要链接的库的路径

bash 复制代码
gcc test.c -l库名 -L库的路径

如上图所示,我们要链接第三方库,带-l选项指明库的名称;如果要链接的库博主系统路径下,带-L选项指明库的路径。

补充:gcc -I选项

在上述操作中,我们的头文件都在当前路径下,如果头文件不在当前路径下呢?

我们把静态库和头文件分别放在./bin/lib./bin/include路径下;

在编译时gcc在当前路径下找不到头文件,就会报错;

解决方法:

  • gcc编译时带-I选项,指明头文件的路径。
  • 在源文件引用头文件时,指明路径;#include "./bin/include/mystdio.h"

动态库

动态库:程序在运行时才会去链接动态库的代码,多个程序可以共享;

一个可执行文件和动态库链接仅仅包含它用到的函数入口地址的一个表。

可执行文件在开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中拷贝到内存中;这一过程称为动态链接

动态库可以被多个程序共享,所以动态链接的可执行文件更下,节省了磁盘空间。

动态库的制作

我们知道了静态库是.o的归档文件,那动态库呢?

动态库又是如何生成的呢?

还是上述的代码mystd.cmystring.cmystdio.hmystring.h

首先生成动态库,也是要先将所有的.c文件编译形成.o文件,与生成静态库不同的是,生成动态库在编译形成.o文件是需要带-fPIC选项,产生位置无关码。

其次,就是将这些.o文件形成动态库,这里使用的是gcc-shared选项

bash 复制代码
gcc -o libmyc.so *.o -shared

动态库的使用

动态库的使用和静态库使用,可以说一模一样的了;

这里就直接演示使用了:

这里我们发现一个问题,我们gcc链接libmyc.so库,编译链接形成了可执行程序a.out,在运行时它找不到libmyc.so库?

通过ldd查看a.out可执行程序依赖的库,可以发现确实找不到libmyc.so库。

这是为什么呢?我们在gcc编译时,使用-L选项不是指明libmyc.so的路径了吗?

这是因为我们gcc编译是-L选项指明libmyc的路径,这是告诉gcc我们要链接的库在哪,但是系统并不知道我们的库在哪里;

因为这里是动态链接,在可执行程序执行时,系统就会去找库libmyc.so,就会发现系统找不到这个库。

运行时搜索路径

那我们知道了动态链接我们自己的库,在可执行程序运行时,系统找不到我们的库,那如何解决这一问题呢?

这里解决方案有很多,我们一一来看:

首先,我们要知道,系统为什么找不到我们自己的库,却可以找到C语言标准库?

因为我们C语言标准库在系统的指定目录下,可执行程序在运行时,系统会在指定路径下去寻找,所以C语言标准库就可以被系统找到。

1. 将我们的库拷贝到系统指定路径下,系统指定路径一般指/usr/lib/usr/local/lib/lib64

2. 在系统指定文件中建立软链接

这里我们库比较小,如果我们的库比较大,拷贝到系统指定路径下很不现实;

所以我们就可以在系统指定路径下建立同名软链接。

3. 更改环境变量LD_LIBRARY_PATH

上面两种方法,都是将我们的库放入(拷贝/软链接)系统指定文件中;

我们还可以通过修改环境变量LD_LIBRARY_PATH,让我们的库能够被系统找到

这里,博主自己的系统配置过vim,没有配置的该环境变量可能就是空了

我们可以修改这个环境变量,把我们库的路径加上去,这样系统就可以找到我们的库libmyc.so了。

4. ldconfig配置

除了上述三种方法之外呢,我们还可以进行配置/etc/ld.so.conf.d/,并更新ldconfig

这样系统也可以找到我们的库libmyc.so

本篇文章的大致内容到这里就结束了,感谢各位大佬的支持

简单总结:

静态库制作: ar -rc将所有.o位置归档。

动态库制作: gcc-shared选项,将所有.o文件形成动态库

库的使用: gcc-l指定链接某些库,-L指明要链接库所在的路径,-I指明头文件所在的路径

可执行程序在运行时,系统找到我们自己库的方法 :将我们的库(拷贝/创建同名软链接)在系统指定路径中、修改环境变量LD_LIBRARY_PATH、配置/etc/ld.so.conf.d/中的文件,并更新ldconfig

相关推荐
金智维科技官方几秒前
RPA如何支持跨平台和跨浏览器的自动化
运维·自动化·rpa
杨了个杨89824 分钟前
Ubuntu搭建DNS服务器
linux·服务器·ubuntu
DIY机器人工房5 分钟前
[9-3] 串口发送&串口发送+接收 江协科技学习笔记(26个知识点)
笔记·科技·stm32·单片机·学习·江协科技
未来之窗软件服务6 分钟前
火狐安装自动录制表单教程——仙盟自动化运营大衍灵机——仙盟创梦IDE
运维·自动化·东方仙盟·自动录入单子·仙盟大衍灵机
檀越剑指大厂9 分钟前
【Docker系列】Docker 容器内安装`ps`命令
运维·docker·容器
步、步、为营22 分钟前
.net Avalonia 在centos部署
linux·centos·.net
Tom Boom3 小时前
39. 自动化异步测试开发之编写异步业务函数、测试函数和测试类(函数写法)
运维·自动化·协程·异步函数·自动化测试开发
文牧之6 小时前
PostgreSQL 临时表空间
运维·数据库·postgresql
李天琦6 小时前
git查看commit属于那个tag
linux·git·云计算
liulilittle6 小时前
关于DDOS
linux·运维·服务器·网络·ddos·通信