动静态库(Linux)
前言
我们之前用过c语言的库.Linux中默认的都是使用动态库,如果想要使用静态库,就必须加上-static选项。默认都是安装的动态库,系统中一般没有静态库,如果要使用,就需要自己安装。
Linux下动态库是以.so结尾的,静态库是以.a结尾的。
无论是动态库还是静态库,都有自己的名字。比如libc.so,库的名字需要去掉前缀lib,去掉后缀.so,那么这就是一个c标准库。
一、静态库
什么是库呢??
库本质就是把一些.o结合放在一起就是我们使用的库
我们只要拥有.o文件和.h文件就可以调用相关的函数。 比如下面的例子
add.h
#pragma once
int add(int x,int y);
add.c
#include "add.h"
int add(int x,int y)
{
return x+y;
}
myfile.c
#include "myfile.h"
//打开
myFILE*myopen(const char*path,const char*mode)
{
int flag=0;
if(strcmp(mode,"w")==0)
{
flag|=O_CREAT|O_WRONLY|O_TRUNC;
}
else if(strcmp(mode,"a")==0)
{
flag|=O_CREAT|O_APPEND|O_TRUNC;
}
else if(strcmp(mode,"r")==0)
{
flag|=O_RDONLY;
}
else
{
return NULL;
}
int fd=0;
if(flag&O_RDONLY)
{
fd=open(path,flag);
}
else
{
umask(0);
fd=open(path,flag,0664);
}
myFILE*fp=(myFILE*)malloc(sizeof(myFILE));
fp->inode=fd;
fp->pos=0;
fp->cap=MAX;
fp->flushmode=FLUSHLINE;
return fp;
}
void myfflush(myFILE*fp)
{
write(fp->inode,fp->buffer,fp->pos);
fp->pos=0;
}
//写文件
int myfwrite(const void *ptr,size_t n,myFILE*fp)
{
//首先放到缓冲区
memcpy(fp->buffer+fp->pos,ptr,n);
fp->pos+=n;
//判断是否需要刷新
if(fp->flushmode==1&&fp->buffer[fp->pos-1]=='\n')
{
myfflush(fp);
}
if(fp->flushmode==2&&fp->pos==fp->cap)
{
myfflush(fp);
}
return n;
}
//关闭
void myclose(myFILE*fp)
{
//进程退出先刷新
myfflush(fp);
close(fp->inode);
free(fp);
}
myfile.h
#pragma once
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define FLUSHLINE 1
#define FLUSHFULL 2
#define FLUSHNO 3
#define MAX 4096
typedef struct myFILE
{
int inode;
char buffer[MAX];
int pos;
int cap;
int flushmode;
}myFILE;
//打开
myFILE*myopen(const char*path,const char*mode);
//写文件
int myfwrite(const void *ptr,size_t n,myFILE*fp);
//刷新
void myfflush(myFILE*fp);
//关闭
void myclose(myFILE*fp);
测试代码test.c
#include <stdio.h>
#include "add.h"
#include "myfile.h"
int main()
{
int x=10;
int y=20;
int sum=add(x,y);
printf("%d+%d=%d\n",x,y,sum);
myFILE*fp=myopen("file.txt","w");
if(fp==NULL)
{
perror("myopen:");
return 1;
}
int cnt=5;
char buffer[64]={"hello world\n"};
while(cnt--)
{
myfwrite(buffer,64,fp);
}
myclose(fp);
return 0;
}
1.正常测试
下面是我们的结构,我们自己写了两个add和myfile两个函数文件1,我们并不像把.c文件给user,但是好像让他用我们的函数,我们就可以把.o和.h给他,然后这个用户自己写main函数就可以。
我们测试一下效果
我们运行看一下结果
确实完成了我们的任务。
2.打包测试
我们同样也可以把这些.o文件进行打包操作,形成一个文件
在静态库下我们用的打包操作是:ar -rc ,注意按照库的格式命名
查看打包库的情况:ar -tv
把打包的库文件和.h拷贝到该用户下,编译运行test.c
我们后发现报错了???这是为什么呢???
我们自己安装的属于第三方库,系统默认回去自己默认的路径去寻找,就不会找到这个库文件,虽然他们在同一路径下。我们需要指明库的路径和库名称,采用下面指令
gcc 文件 -L(跟路径) -l(跟库名称)
为什么要有库呢??测试目标文件生成之后,静态库就可以删掉了,程序照样可以正常运行
为什么要有库呢??
1.提高开发效率
2.隐藏源代码
二、动态库
同样还是上面的代码,我们继续测试动态库
1.正常测试
我们如果想要对动态库进行编辑,需要带上 -fPIC(产生位置无关码)选项
我们如果想对这些.o文件进行打包,我们需要借助-shared(动态格式)选项,对动态库打包不需要其他选项,直接使用gcc就可以
此时我们就可以正常对test.c进行编译。但是会报错
此时就与静态库相同,动态库也属于第三方库,需要指定库的路径和库名称
我们平时使用的库都是把.o文件和.h文件进行打包,上传到网络中,使用者将库下载下来,进阶捷报就可以正常操作了。
下面我们模拟一下这个过程
makefie中内容
libmyc.so:add.o myfile.o
gcc -shared -o $@ $^
%.o:%.c
gcc -c -fPIC $<
.PHONY:clean
clean:
rm -rf *.o libmyc.so mylib mylib.tgz
.PHONY:output
output:
mkdir -p mylib/include
mkdir -p mylib/lib
cp -rf *.h mylib/include
cp -rf *.so mylib/lib
tar czf mylib.tgz mylib
2.安装测试
我们进行make output操作将相应的内容进行打包
用户把这个包下载下来,并且解压到自己的路径下,之后把这个压缩包删除就可以。
所谓的把库安装到系统中,本质就是把对应的文件拷贝到系统指定的路径中。
下面我们再次进行编译,我们可以发现报了一个这样的错误
头文件找不到,Linux下查找头文件会在当前路径和库对应的路径进行查找,我们的.h文件不在在下级路径下,系统自然就找不到。
我们需要用到-I(大写i)指定头文件路径,同时用-L(指定库路径)和-l(指定库名称)
3.运行测试
我们正常运行看一下
发现报错了??这是为什么呢??
编译时我们确定了编译环境所需要的路径以及一系列东西,但是对于运行程序的进程来说,并不知道库的路径(第三方库)。我们也可以用ldd查看链接了哪些库
我们要解决这个有四种方法 🌟直接把所使用的库拷贝到系统指定的路径下,一般是/usr/lib(sudo提权)
🌟使用环境变量LD_LINRARY_PATH,这个环境变量是指,操作系统查找库文件时候,除了去系统指定的目录去查找,同时也会去这个路径下查找
但是如果我们关掉机器,重新打开,这个环境变量就会消失。如果想让他一直存在,可以在跟目录下的_bash_profile和.bashrc修改即可🌟使用软链接的方式
🌟设置配置文件/etc/ld.so.conf.d
将库的路径放在这个目录中,目录名称必须以.conf结尾,之后使用ldconfig使配置生效
4.动态库&&静态库
🌟如果动态库和静态库同时存在,系统自动使用动态库,如果非要使用静态库,加-static选项
🌟如果我们所用的文件只存在静态库,系统将把这个链接静态库,其余正常链接静态库
🌟如果我们只提供动态库,非得想要静态链接,会报错
三、深入理解动态库
1.系统角度
运行代码,首先创建task_struct ,进程地址空间,将磁盘中test.c文件加载进内存,填写页表内容。动静态库在磁盘中也是一个个的文件,也要加载到内存,放在页表中,与物理内存建立映射。
🌟静态库:把test.c和libmyc.o一起加载到内存中,放在一起。运行时就不需要寻找库了。
🌟动态库:把test.c和libmyc.sof分别加载到内存中,其中动态库会被放在进程地址空间的共享区中。当调用这个库时,去共享区中查找,如果存在这个库,就跳转到共享库中调用。如果不存在就加载到内存中。如果其他文件要调用这个动态库,就直接去调用即可,不用再进行加载。动态库也叫做共享库,本质就是进程中代码和数据仅存在一份。补充: 1.进程地址空间的内容初始化是根据文件的代码数据进行填充的。
2.进程同样要对这些库进行管理,怎么管理呢??先描述,在组织
3.那些库加载了,那些库没有被加载,由操作系统自动完成。
2.编译角度
在磁盘中的文件中代码和数据在没有加载进入内存之前,也有自己对应的地址,数据和代码按照一定的格式进行存储。我们就可以用这个初始化我们进程的进程地址空间了。
编址方式有两种:绝对地址和相对地址,机器一般使用绝对编制的方式。
总结
以上就是今天要讲的内容,本文仅仅详细介绍了 。希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~ 😘 😘 😘
原文地址:https://blog.csdn.net/lim6ere/article/details/142530898
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!