Linux:库制作与原理(一)

之前我们一直谈论动静态库,但是我们并没有真正系统学习什么是库,以及库怎么制作,这个系列博客将为你一一解答有关于linux库的知识

1.什么是库

库是写好的现有的,成熟的,可以复⽤的代码。现实中每个程序都要依赖很多基础的底层库,不可能 每个⼈的代码都从零开始,因此库的存在意义⾮同寻常。
本质上来说库是⼀种可执⾏代码的⼆进制形式,可以被操作系统载⼊内存执⾏。库有两种:
静态库 .a[Linux]、.lib[windows]
动态库 .so[Linux]、.dll[windows]
如果你使用win系统,或许有这样的报错

这就说明改软件动态库缺失

2.什么是静态库

静态库(.a):程序在编译链接的时候把库的代码链接到可执⾏⽂件中,程序运⾏的时候将不再 需要静态库⼀个可执⾏程序可能⽤到许多的库,这些库运⾏有的是静态库,有的是动态库,⽽我们的编译默认为动态链接库,只有在该库下找不到动态.so的时候才会采⽤同名静态库。我们也可以使⽤ gcc 的 -static 强转设置链接静态库。

3.静态库的制作

我们先创建一个目录,我们用一个生动形象的例子来讲述静态库与库的意义

加入我们有一个舍友叫做张三,老师给我们布置了一个作业,要求我们写我们自己的库函数的接口

下面是我们自己写的lib:

mystdio.h:

cpp 复制代码
#pragma once 
#include <stdio.h>

#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:

cpp 复制代码
#include "mystdio.h"
#include <sys/types.h>
#include <sys/stat.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, "r") == 0)
    {
        flag = O_RDONLY;
        fd = open(path, flag);
    }
    else if (strcmp(mode, "a") == 0)
    {
        flag = O_CREAT | O_WRONLY | O_APPEND;
        fd = open(path, flag, 0666);
    }
    else 
    {
        //TODO
    }
    if (fd < 0)
    {
        return NULL;
    }
    return BuyFile(fd, flag);
}

void MyFclose(MyFile* file)
{
    MyFFlush(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;
    file->bufferlen = 0;
}

mystring.h:

cpp 复制代码
#pragma once 

int mystrlen(const char* str);

mystring.c:

cpp 复制代码
#include "mystring.h"

int mystrlen(const char* str)
{
    const char* start = str;
    while (*str)
    {
        str++;
    }
    return str - start;
}

现在张三不想做作业,想直接来copy我们的代码,自己只需要写一个usercode就行,于是我们直接cp给他

code.c:

cpp 复制代码
#include "mystdio.h"
#include "mystring.h"
#include <string.h>
#include <unistd.h>
int main()
{
    MyFile* filep = MyFopen("./log.txt", "a");
    if (!filep)
    {
        perror("filep open\n");
        return 1;
    }
    //char* message = (char*)"hello my_libc\n"; //带/n行刷新
    char* message = (char*)"hello my_libc!!!"; //不带就是全刷新
    //MyFwrite(filep, message, strlen(message));
    int cnt = 10;
    while (cnt--)
    {
        MyFwrite(filep, message, strlen(message));
        printf("filep->outbuffer : %s\n", filep->outbuffer);
        sleep(1);
    }
    MyFclose(filep);
    const char* str = "hello world!\n";
    printf("strlen : %d\n", mystrlen(str));
    return 0;
}

于是张三成功通过作业,但是好景不长,老师发现那么两个写的.c一模一样,明显是抄袭的,所以处罚了你们两个

过了一段时间,老师又布置了这个作业,张三依旧想你帮助他,并且请你吃饭作为补偿,但是经过上一次的事情,你决定只给他.o文件,于是有了下面操作

这次老师不知道.c是什么,所以也不知道你们两个是否抄袭

这也说明了一件事情,无论是静态库还是动态库,本质上都是源文件对应的.o文件

经过这件事情,张三很开心,因为他以后只需要你的.o文件即可完成作业,所以他说以后作业都给你吧,随着时间推移,老师布置作业越来越多,直到有一天,张三在外面被老师叫到办公室询问作业情况,而此次作业你写了几百上千个.o文件,如果我们一个一个发给他,那他肯定就完蛋了,因为时间紧急,而一个一个下要下成百数千次,时间来不及,那怎么办呢,此时我们只需要将所有.o打一个包 交付给张三即可,这就是静态库

这么打包呢,或许你会想到我们之前学的tar,但是解包之后依然是成百上千个.o文件,还是需要一个一个下载

所以为了解决这种情况,linux有一个命令,叫做ar(英语:archive)

此时我们就将他打包成立libmyc.a,此时张三只需要头文件,并且不需要解包,就可以直接编译,

解决燃眉之急!!

那张三该怎么使用呢??下面来操作

只需要输入命令gcc code.o -L . -l myc即编译链接

下面是各个命令行参数的意思:

-I:指定头文件路径

-L: 指定库路径
-l: 指定库名
测试⽬标⽂件⽣成后,静态库删掉,程序照样可以运⾏
库⽂件名称和引⼊库的名称:去掉前缀 lib ,去掉后缀 .so , .a ,如: libc.so -> c,libmyc.a -> myc
随着时间的推移,我们越来越火了,越来越多的人来找我们索要libmyc.a包,不仅如此我们还要给他们头文件,以及解释各种使用,如何编译,这也太麻烦了,于是我们想到了一种更好的方法,直接将我们需要的文件压缩为压缩包,到时候将压缩包挂在网上,需要的自己去下载,而且我们在压缩包里面附带include(头文件)与 .a(需要的库)

此时我们创建了一个lib,需要使用的人只需要下载即可
此时张三开始解压缩(tar xzf lib.tgz)并且使用

此时张三只需要这样编译即可运行

或许你会说怎么这么麻烦,为什么我使用c语言库函数就不需要这么麻烦,只需要gcc code.c即可,那是因为系统会默认去系统的lib与include分区去找对应的库与头文件,所以一般我们需要把我们写的库与头文件cp到指定系统文件才可以使用gcc code.c -l myc,为什么这里还需要-l myc呢,这是因为系统只认识原本的lib内容,对于后来新加的,系统需要你的确定才放心使用

好啦,这就是有关于静态库的知识啦,下一篇博客我们将学习动态库,敬请期待哦~~

相关推荐
落羽的落羽2 小时前
【C++】深入浅出“图”——图的基本概念与存储结构
服务器·开发语言·数据结构·c++·人工智能·机器学习·图搜索算法
秋深枫叶红2 小时前
嵌入式第三十九篇——linux系统编程——信号通信、共享内存
linux·运维·服务器·学习
咸鱼加辣2 小时前
【nginx面试题】nginx虚拟
运维·nginx·github
乌萨奇也要立志学C++2 小时前
【Linux】线程互斥与互斥量全解析:原理、实践与封装
linux·服务器
hweiyu002 小时前
Linux命令:gzip
linux
老王熬夜敲代码2 小时前
IP和MAC的深入理解
linux·网络·笔记·网络协议
梁辰兴2 小时前
计算机网络基础:以太网的信道利用率
服务器·网络·计算机网络·计算机·以太网·信道利用率·梁辰兴
开开心心就好2 小时前
版本转换工具,支持Win双系统零售批量版
linux·运维·服务器·pdf·散列表·零售·1024程序员节
秋深枫叶红2 小时前
嵌入式第三十八篇——linux系统编程——IPC进程间通信
linux·服务器·网络·学习