库的制作与原理(一)

news2025/2/23 8:16:43

·1.库的概念

库是写好的,现成的可以复用的代码。本质上库是一种可执行的二进制形式,可以被操作系统载入内存执行。库有俩种:静态库 .a[Linux]   .lib[windows]       动态库 .so[Linux]  .dll[windows]

就是把.c文件变成.o文件,把许多的.o文件一起打包成.a或者.so。

需要用到的代码

my_stdio.h

#pragma once
#define SIZE 1024

#define FLUSH_NONE 0
#define FLUSH_LINE 1
#define FLUSH_FULL 2

struct IO_FILE
{
        int flag;//刷新方式
        int fileno;//文件描述符
        char outbuffer[SIZE];
        int cap;
        int size;
};

typedef struct IO_FILE mFILE;

mFILE* mfopen(const char* filename,const char* mode);
int mfwirte(const void* ptr,int num,mFILE* stream);
void mfflush(mFILE* stream);
void mfclose(mFILE* stream);

my_stdio.c

#include"my_stdio.h"
#include<string.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>



mFILE* mfopen(const char* filename,const char* mode)
{
        int fd=-1;
        if(strcmp(mode,"r")==0)
        {
                fd=open(filename,O_RDONLY);
        }
        else  if(strcmp(mpde,"w")==0)
        {

                fd=open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
        }
        else if(strcmp(mode,"a")==0)
        {
                fd=open(filename,O_CREAT|O_WRONLY|O_APPEND,0666);
        }
        if(fd<0)
        {
                return NULL;
        }
        mFILE* mf=(mFILE*)malloc(sizeof(mFILE));
        if(!mf)
        {

                close(fd);
                return NULL;
        }

        mf->fileno=fd;
        mf->flag=FLUSH_LINE;
        mf->size=0;
        mf->cap;
        return mf;


}

void mfflush(mFILE* stream)
{
        if(stream->size>0)
        {
                //写到内核文件的缓冲区中
                write(stream->fileno,steam->outbuffer,stream->size);
                //用fsync函数稳定刷新到外设
                fsync(stream->fileno);
                stream->size=0;


        }

}

int mfwrite(const void* ptr,int num,mFILE* stream)
{
        memcpy(stream->outbuffer+stream,ptr,num);
        stream->size+=num;
        //检查是否更新
        if(stream->flag==FLUSH_LINE&&stream->size>0&&stream->outbuffer[stream->size-1]=='\n')
        {
                mfflush(stream);
        }
        return num;



}

void mfclose(mFILE* stream)
{
        if(stream->size>0)
        {
                mfflush(stream);
        }
        close(stream->fileno);


}

 my_string.h

#pragma once

int my_strlen(const char* s);

my_string.c


#include"my_string.h"

int my_strlen(const char* s)
{
        const char* end=s;
        while(*end!='\0')end++;
        return end-s;
}

fsync 函数是 POSIX 标准中定义的一个函数,用于将文件描述符指向的文件的数据强制写入到磁盘上。这个函数确保了所有缓冲区中的数据都被写入到磁盘,并且磁盘上的数据是最新的。这对于需要确保数据持久化的应用场景非常重要,例如数据库、日志文件等。

2.静态库

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

一个可执行程序可能用到许多库,这些库运行有的是静态库,有的是动态库,而我们编译默认认为动态链接库,只有在该库找不到动态.so的时候才会采用同名静态库。可以使用gcc的-static强转设置链接静态库。

静态库的生成

makefile文件

libmystdio.a:my_stdio.o my_string.o
        @ar -rc $@ $^
        @echo "bulid $^ to  $@"



%.o:%.c
        @gcc -c $<
        @echo "compling $< to $@"

.PHONY:clean
clean:
        @rm -rf *.a *.o stdc*


.PHONY:output
output:
        @mkdir -p stdc/include
        @mkdir -p stdc/lib
        @cp -f *.h stdc/include
        @cp -f *.a stdc/lib
        @tar -czf stdc.tgz stdc

ar是gun归档工具,rc表示replace and create

静态库的使用

#include "my_stdio.h"
#include "my_string.h"
#include <stdio.h>
int main()
{
        const char *s = "abcdefg";
        printf("%s: %d\n", s, my_strlen(s));
        mFILE *fp = mfopen("./log.txt", "a");
        if(fp == NULL) return 1;
        mfwrite(s, my_strlen(s), fp);
        mfwrite(s, my_strlen(s), fp);
        mfwrite(s, my_strlen(s), fp);
        mfclose(fp);
        return 0;
}
~        

场景一:头文件和库文件安装到系统默认路径下时

指令:gcc main.c -lmystdio 

场景二:头文件和库文件和源文件在同一路径下时

指令:gcc main.c -L. -lmymath

场景三:头文件和库文件有自己的独立路径

gcc main.c -I头文件路径 -L库文件路径 -lmymath(链接库的名称)

注意:库的名称要去掉前面的lib和后缀才是名字 

3.动态库

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

一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机械码。

动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间,操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有1进程共享,节省了内存和磁盘空间。

动态库的生成


libmystdio.so:my_stdio.o my_string.o
        @gcc -o $@ $^ -shared
        @echo "bulid $^ to  $@"



%.o:%.c
        @gcc -fPIC -c $<
        @echo "compling $< to $@"

.PHONY:clean
clean:
        @rm -rf *.so *.o stdc*


.PHONY:output
output:
        @mkdir -p stdc/include
        @mkdir -p stdc/lib
        @cp -f *.h stdc/include
        @cp -f *.so stdc/lib
        @tar -czf stdc.tgz stdc

shared:表示生成共享库格式

fPIC:产生位置无关码(position independent code)

库名规则:libxxx.so

动态库使用

场景一:头文件和库文件安装到标准系统路径下

gcc main.c -lmystdio 

场景二:头文件和库文件与源文件在同一个路径下

gcc main.c -L. -lmymath 

场景三:头文件和库文件都有自己的独立路径

gcc main.c -I头文件路径 -L库文件路径 -lmymath 

 4.库运行搜索路径

问题:

按照指令却显示问题,是因为链接器没有正确找到库文件的路径

 

解决方案

拷贝.so文件到系统共享库路径下,一般指/user/lib,或者指明完整库路径,例如拷贝到/lib64路径下

先系统共享路径下建立软连接,在/lib64下建立一个与库文件的软连接,先找到软连接再找到库

更改环境变量:LD_LIBRARY_PATH,这个通常是空的,OS运行程序,要查找动静态库,也会在该环境变量, 把完整库的路径赋值给这个环境变量

ldconfig方案:配置/etc/ld.so.conf.d/,ldconfig更新 ,这个路径系统会提前准备打开,把库路径放进去就会1提前准备好,在/etc/ld.so.conf.d/路径下配置好文件还要ldconfig更新一下。

5.目标文件

编译和链接这俩个步骤,在windows下被IDE封装的很完美。

 

 编译的过程:将程序的源代码翻译成CPU能够直接运行的机器代码。

为什么要有目标文件?假如有多个原文件,直接就合在一起编译,要是其中有一个要修改,则又要重新全部在和一起就浪费效率了,而每个都形成目标文件,改了一个也只要单独编译这一个,不影响其它。目标文件是一个二进制文件,文件的格式是ELF,是对二进制代码的一种封装。

6.ELF文件

以下四种文件都是ELF文件:

可重定位位文件:xxx.o文件

可执行文件

共享目标文件:xxx.so文件

内核转储

一个ELF文件由以下四部分组成:

ELF头:描述文件的主要特性。位于文件的开始位置,主要目的是定位文件的其它部分。

程序头表:列举了所有有效的段和它们的属性。表里记着每个段的开始位置和位移,长度,因为放在二进制文件中,需要段表的描述信息才能把它们每个段分隔开。

节头表:包含对节的描述

节:ELF文件中的基本组成单位,包含了特定类型的数据,ELF文件的各种学习和数据存储在不同的节中,如代码节存储了可执行代码,数据节存储了全局变量和静态数据等。

最常见的节:

代码节(.text) 数据节(.data)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2303833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

go 日志框架

内置log import ("log""os" )func main() {// 设置loglog.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)// 自定义日志前缀log.SetPrefix("[pprof]")log.Println("main ..")// 如果用format就用PrintF&#xff0c;而不是…

Rust配置笔记

1.Node.js下载配置 2.c环境配置 C我是用vs装的点击这个installer 点击修改 选择C环境就行,这个时候它就帮忙配置环境了 3.Rust下载配置 4.装napi-rs框架 npm install -g napi-rs/cliRust下载网站 下完之后直接打开 一开始下包会比较慢,多等等 下好之后跑项目前第一件事配置…

具有整合各亚专科医学领域知识能力的AI智能体开发纲要(2025版)

整合各亚专科医学领域知识能力的AI代理的开发与研究 一、引言 1.1 研究背景 在科技飞速发展的当下,人工智能(AI)已成为推动各行业变革的关键力量,医疗领域也不例外。近年来,AI 在医疗行业的应用取得了显著进展,从医学影像诊断到疾病预测,从药物研发到个性化医疗,AI 技…

数据表的存储过程和函数介绍

文章目录 一、概述二、创建存储过程三、在创建过程中使用变量四、光标的使用五、流程控制的使用六、查看和删除存储过程 一、概述 存储过程和函数是在数据库中定义的一些SQL语句的集合&#xff0c;然后直接调用这些存储过程和函数来执行已经定义好的SQL语句。存储过程和函数可…

为AI聊天工具添加一个知识系统 之117 详细设计之58 思维导图及观察者效应 之2 概念全景图

&#xff08;说明&#xff1a;本文和上一篇问题基本相同&#xff0c;但换了一个模型 deepseek-r1&#xff09; Q1227、在提出项目“为使用AI聊天工具的聊天者加挂一个专属的知识系统”后&#xff0c;我们已经进行了了大量的讨论-持续了近三个月了。这些讨论整体淋漓尽致体现了…

Error [ERR_REQUIRE_ESM]: require() of ES Module

报错信息&#xff1a; 【报错】Message.js 导入方式不对&#xff0c;用的是 ES Moudle 的语法&#xff0c;提示使用 import 引入文件 项目开发没有用到 js-message 依赖&#xff0c;是 node-ipc 依赖中用到的 js-message 依赖&#xff0c; node-ipc 中限制 js-message 版本&a…

GStreamer源码安装1.24版本

从官网下载 1.24的源码包 https://gitlab.freedesktop.org/gstreamer/gstreamer/-/tree/1.24?ref_typeheads#getting-started &#xff0c;尝试过使用git clone 的方式&#xff0c;但速度贼慢&#xff0c;就选择了下载源码包的方式安装依赖 sudo apt install libssl-dev g me…

从CNN到Transformer:遥感影像目标检测的未来趋势

文章目录 前言专题一、深度卷积网络知识专题二、PyTorch应用与实践&#xff08;遥感图像场景分类&#xff09;专题三、卷积神经网络实践与遥感影像目标检测专题四、卷积神经网络的遥感影像目标检测任务案例【FasterRCNN】专题五、Transformer与遥感影像目标检测专题六、Transfo…

从 x86 到 ARM64:CPU 架构的进化与未来

在计算机发展的历史长河中&#xff0c;x86、x64 和 ARM64 这三大主流 CPU 架构各自书写了辉煌的篇章。它们不仅代表了技术的进步&#xff0c;更承载着无数创新者的梦想与努力。 x86&#xff1a;从 16 位到 32 位的辉煌之路 诞生与崛起 1978 年&#xff0c;英特尔&#xff08;…

Java数据结构第十二期:走进二叉树的奇妙世界(一)

专栏&#xff1a;数据结构(Java版) 个人主页&#xff1a;手握风云 目录 一、树型结构 1.1. 树的定义 1.2. 树的基本概念 1.3. 树的表示形式 二、二叉树 2.1. 概念 2.2. 两种特殊的二叉树 2.3. 二叉树的性质 2.4. 二叉树的存储 三、二叉树的基本操作 一、树型结构 1.…

【AI时代】基于AnythingLLM+ Ollama + DeepSeek 搭建本地知识库

一、本地安装Ollama及DeepSeek 参考教程&#xff1a; https://blog.csdn.net/Bjxhub/article/details/145536134 二、下载并安装AnythingLLM AnythingLLM下载地址&#xff1a; https://anythingllm.com/ 傻瓜式安装即可 可以自定义安装路径。三、配置AnythingLLM并使用 3.…

leetcode刷题记录(一百一十五)——64. 最小路径和

&#xff08;一&#xff09;问题描述 64. 最小路径和 - 力扣&#xff08;LeetCode&#xff09;64. 最小路径和 - 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。说明&#xff1a;每次只能向下…

UE5 编辑器辅助/加强 插件搜集

1. Actor Locker 地址&#xff1a;https://www.fab.com/listings/ec26ac5e-4720-467c-a3a6-b5103b6b74d0 使用说明&#xff1a;https://github.com/Gradess2019/ActorLocker 支持&#xff1a;5.0 – 5.5 简单的编辑器扩展。它允许你通过世界轮廓窗口/热键/上下文菜单在编辑器视…

怎么在Github上readme文件里面怎么插入图片?

环境&#xff1a; Github 问题描述&#xff1a; 怎么在Github上readme文件里面怎么插入图片&#xff1f; https://github.com/latiaoge/AI-Sphere-Butler/tree/master 解决方案&#xff1a; 1.相对路径引用 上传图片到仓库 将图片文件&#xff08;如 .png/.jpg&#xff…

什么是矩阵账号?如何高效运营tiktok矩阵账号

‍‌​​‌‌​‌​‍‌​​​‌‌​​‍‌​​​‌​‌​‍‌​​‌​​‌​‍‌​‌‌​‌‌‌‍‌​‌​‌​​​‍‌​​‌​‌‌​‍‌​​​​‌‌​‍‌​‌​​‌‌‌‍‌​​‌‌​‌​‍‌​‌​​‌‌‌‍‌​‌‌‌​​‌‍‌‌​​‌‌‌​‍‌‌​​‌‌​​‍‌…

SpringSecurity初始化的本质

一、对SpringSecurity初始化的几个疑问 通过前面第一次请求访问的分析我们明白了一个请求就来后的具体处理流程 对于一个请求到来后会通过FilterChainProxy来匹配一个对应的过滤器链来处理该请求。那么这里我们就有几个疑惑。 FilterChainProxy什么时候创建的?过滤器链和对应的…

自注意力机制和CNN的区别

CNN&#xff1a;一种只能在固定感受野范围内进行关注的自注意力机制。​CNN是自注意力的简化版本。自注意力&#xff1a;具有可学习感受野的CNN。自注意力是CNN的复杂形态&#xff0c;是更灵活的CNN&#xff0c;经过某些设计就可以变为CNN。 越灵活、越大的模型&#xff0c;需要…

本地部署DeepSeek-R1模型教程

文章目录 第一步&#xff1a;安装运行框架 哈喽各位小伙伴们&#xff0c;最近deepseek非常的火&#xff0c;不过因为全球访问量剧增经常会导致deepseek的服务器繁忙&#xff0c;如果想要稳定使用就得靠本地部署了&#xff0c;不仅可以稳定使用还能保护数据隐私&#xff0c;今天…

k2路由器登录校园网

教程1刷入Breed&#xff0c;并手动刷入Padavan固件&#xff1a;斐讯K1、K2、K2P 刷机、刷入Breed 辅助工具 | tb (tbvv.net) Padavan下载网址&#xff1a; 我用的是&#xff1a; Padavan 登录的网址是 192.168.123.1 Padavan配置教程&#xff1a; 先用网线连上校园网&#…

基于Springboot学生宿舍水电信息管理系统【附源码】

基于Springboot学生宿舍水电信息管理系统 效果如下&#xff1a; 系统登陆页面 系统用户首页 用电信息页面 公告信息页面 管理员主页面 用水信息管理页面 公告信息页面 用户用电统计页面 研究背景 随着高校后勤管理信息化的不断推进&#xff0c;学生宿舍水电管理作为高校后勤…