【Linux】软硬连接 | 静动态库
🪐🪐🪐欢迎来到程序员餐厅💫💫💫
主厨的主页:Chef‘s blog
所属专栏:青果大战linux
你说得对,但是这就是期末临近,一遍学操作系统,一边琢磨怎么复习自己的专业课,还有六级!!!!!
软链接
使用下面的指令即可生成软链接文件
基本语法:ln -s [目标文件或目录路径] [软链接文件名]
概念
可以看到我们生成了一个软链接文件,文件后缀可以随便写,因为linux解析大多数文件中不会考虑文件后缀,但是为了方便我们自己认识还是规范写比较好。
上图红框的第一个字母是l,表示这是一个软链接文件
软链接生成的是一个独立文件,因为他有自己独立的inode
软链接的文件内容是所指向的目标文件的路径,可以理解为windows中的快捷方式,你可以通过该软链接文件打开目标文件。
如上图,我们在一个文件中输入字符串,之后可以通过该文件的软链接文件访问它。
请注意输入目标文件的路径时建议用用绝对路径,因为软链不会解析那个相对路径,举个例子,
[qingguo@host project23_link]$ sudo ln -s ./t1 /usr/bin/t3
我通过相对路径在usr/bin目录下建立t1的软链接,那么软链接链接的路径就不是此时的./即/home/qingguo/project23_link/,而是链接过去之后的./即usr/bin。
可以看到链接后的文件表示找不到目标文件
让后我把目标文件移动到了 软链接文件的目录即./usr/bin下,此时就表示可以使用了。
用途
- 1.对文件
就像我们在window中使用快捷键一样,要使用一个文件但是他藏在很深的目录里,找起来太麻烦了,于是你可以写个软链接把该软连接文件统一放到一个地方,使用就方便了(比如桌面)。
- 2.对目录
同样的加入有个目录下的东西你经常需要看,但是目录很长,输入起来很麻烦,你可以可以对该目录进行软链接,这样就不用输入那一长串目录,只用输入软链接的文件名了.
[qingguo@host ~]$ ln -s /usr/bin ub
[qingguo@host ~]$ sudo ll ub
- 3.伪装指令
通过这种方式你也可以把自己的可执行程序放到usr/bin目录下,就可以当指令用了
删除软链接文件
要删除软链接文件可以直接rm,也可以使用unlink+软链接文件(带路径)
硬链接
基本语法:ln [目标文件或目录路径] [硬链接文件名]
概念
可以看出生成方式上,软硬链接只差了一个-s
可以看出生成的硬链接文件的inode和原文件一样 ,并且硬链接后红框圈住的数字加了1
我们不认为硬链接文件是个独立的文件,毕竟他没有独立的inode,他本质就是所处目录下新增的一组已存在的文件名与inode的映射关系。
而那个红框圈的数字就是硬链接数,表示有多少文件名指向该文件的inode,所以要删除一个文件不是删一个文件就够了,而是要把和该文件有映射的所有文件都删掉,这里采用的就是引用计数。
我们删掉了原文件,此时软链接失效,但是我们依据可以依靠硬链接访问该文件的内容,这就是引用计数删除的体现。
可以发现,一个独立的文件没有对他使用硬链接时硬链接数为1,但是文件夹的硬连接数却是2
因为目录有一个隐藏文件".",该文件也是指向该文件夹的,所以硬链接数为2
接着我在该dir1里有新建了一个文价夹,dir1的硬连接数加1了。
因为新建的文件夹里有隐藏文件"..",它指向上级目录即dir1,
这些"."和".."采用的就是硬链接。
这里我们就可以解释为什么根目录再去cd .. 就不能继续前进了,因为他的".." 指向的文件就是自己本身,和"."是一样的。看下图根目录下的“.”和“..”的inode都是2。
用途
- 1.备份文件
显然我们直前学的备份就是cp指令,这个指令的备份是新建了一个文件,这显然增加了空间开销,现在我们就可以使用硬链接进行备份,空间开销约等于0,优雅、真是太优雅了。
链接目录
硬链接不可以链接目录,原因是硬链接目录会形成环状路径,
这样的路径,请问A-hard.link的上级目录算是C还是根目录,他的下一级目录是B还是E和F,这就没法解析目录了啊,所以OS禁止用户硬链接目录,这时候就有人问了:
我们不是刚讲了“.”和“..”这两个隐藏文件就是硬链接,而且是对目录的硬链接,这不矛盾了吗
没错,OS就是这样,在这个硬链接文件目录上,只许州官放火不许百姓点灯,因为他自己其实还加了一些特殊处理,我们知道这个事就行。
软链接则不然,我们可以tree一下
显然,OS对软链接的文件不会直接展开他链接的文件,而是就先把他当作一个普通文件看待 ,这样就可以链接目录啦。
静态库
ldd指令可以查看一个文件链接的库
准备工作
首先我们先完成源码,自己写俩头文件以及函数实现,等下把他们封装成库来使用
- mystdio.c
#include"mystdio.h"
#include<unistd.h>
#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
mFILE* mfopen(const char*filename,const char*mode){
int fd=-1;
if(*mode=='r'){
fd= open(filename,O_RDONLY);
}
else if(*mode=='w'){
fd= open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
}
else if(*mode=='a'){
fd=open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);
}
mFILE*f=(mFILE*)malloc(sizeof (mFILE));
if(!f){
close(fd);
return NULL;
}
f->fd=fd;
f->flag=FLUSH_FULL;
f->cap=1024;
f->size=0;
return f;
}
int mfwrite(char*str,int num,mFILE*stream){
memcpy(stream->buffer+stream->size,str,num);
stream->size+=num;
int flag=1;
if(stream->flag==FLUSH_LINE){
if(stream->size>0&&stream->buffer[stream->size-1]=='\n'){
flag=write(stream->fd,stream->buffer,stream->size);
stream->size=0;
}
}
else if(stream->flag==FLUSH_FULL){
if(stream->size>stream->cap*9/10){
flag=write(stream->fd,stream->buffer,stream->size);
printf("%d",stream->size);
stream->size=0;
}
}
return flag;
}
void mfflush(mFILE*stream){
write(stream->fd,stream->buffer,SIZE);
}
int mfclose(mFILE*stream){
mfflush(stream);
return close(stream->fd);
}
- mystdio.h
#pragma once
#define SIZE 1024
#define FLUSH_NODE 0
#define FLUSH_LINE 1
#define FLUSH_FULL 2
struct mFILE{
int fd;
int flag;//文件刷新方式
char buffer[SIZE];
int cap;
int size;
};
typedef struct mFILE mFILE;
mFILE* mfopen(const char*filename,const char*mode);
int mfwrite(char*str,int num,mFILE*stream);
void mfflush(mFILE*stream);
int mfclose(mFILE*stream);
- mystring.h
#include"mystring.h"
int mstrlen(const char*arr){
int i=0;
while(arr[i])
i++;
return i;
}
- mystring.c
#pragma once
int mstrlen(const char*arr);
- main.c
#include<mystdio.h>
#include<stdio.h>
#include<mystring.h>
int main(){
printf("%d",mstrlen("aaa"));
mFILE*p=mfopen("t1.txt","w");
mfclose(p);
}
介绍
【基本语法】
ar -rc libname.a [要打包的.o文件]
- ar事archive的缩写,意思是把。。归档
- -r表示repalce,即若库中已有同名文件则进行替代
- -c表示creat,若库中没有该文件则创建
什么是静态库
静态库(Static Library),在计算机编程领域,是一种将多个目标文件(通常是编译后的代码)打包在一起的文件格式。它的扩展名在不同的操作系统和编译器下可能有所不同,例如在 Unix/Linux 系统中常为
.a
(archive 的缩写),在 Windows 系统中常为.lib
。静态库就像是一个代码仓库,里面包含了一系列可以被其他程序调用的函数和变量的编译后版本
在理解这些后,我们可以写一个makefile,它可以创建一个库,把库和头文件放到目标路径,以及清理所有数据
%.o:%.c
gcc -c $<//.o文件的实现方法写哪里都可以,如果在执行某条命令时发现依赖文件没有生成,系统会自动检查makefile中的所有指令看有没有用于生成该依赖文件的
libmystdio.a:mystdio.o mystring.o
ar -rc $@ $^
.PHONY:clean
clean:
rm -rf *.a *.o stdc
.PHONY:output
output:
mkdir -p stdc/include//注意要先写mkdir,再写cp指令,这里是按照顺序执行的
mkdir -p stdc/lib
cp -rf *.c stdc/include
cp -rf *.a stdc/lib
tar -czf stdc.tgz stdc//顺带打个包压缩一下,可以直接发给需要的同学(太优雅了)
使用方法
- 1.头文件和库都在标准路径下
把头文件放到usr/bin目录下,库文件放到/lib64
直接gcc编译链接
gcc -o main main.c
好了,不出意外就会报错,我来给你分析一下
对于头文件如果是以<>的方式包含,那么系统会自动去标准路径及usr/bin目录下挨个找该头文件是否存在,
对于库,OS会在默认标准路径即/lib64下查找,但是你要告诉他要找的库名字!
-l +库名字表示去找该库
注意库的名字是去掉.a或.so的后缀,去掉lib前缀
gcc -o main main.c -l mystdio
也可以这么写
gcc -o main main.c -lmystdio
这时候就有人问了,为什么我们平常用gcc编译链接标准库就不用-l+库名字
老弟,你猜猜他为什么叫gcc?
在编译 C 程序时,GCC(GNU Compiler Collection)对于 C 标准库有隐式链接的机制。C 标准库是非常基础且常用的库,几乎每个 C 程序都会用到其中的一些函数,如
stdio.h
中的printf
、scanf
等。为了方便开发者,GCC 编译器默认会自动链接 C 标准库
g++也是同理
- 2.库不放到标准路径下
-L +路径表示除了系统路径,也要去这个路径下找库
gcc -o main main.c -lmystdio -L ./my
- 库和头文件.h都不在标准路径下
-I +路径表示除了系统路径,也要去这个路径下找头文件
gcc -o main main.c -lmystdio -L ./my -I ./include
动态库
生成动态库
gcc -o 动态库名称 [依赖的.o文件] -shared
生成需要的.o文件
gcc -fPIC -o .o文件名称 -c 源文件名称
显然我们这次多了一个-fPIC(Position - Independent Code)选项,翻译一下就是位置无关代码,他的作用是使得生成的.o文件代码是位置无关的,这是因为动态库在内存中的加载位置是不确定的,需要代码能够在任何位置正确执行。关于这个问题我们下节课会细讲。
libmystdio.so:mystdio.o mystring.o
gcc -o $@ $^ -shared
%.o:%.c
gcc -fPIC -o $@ -c $<
.PHONY:clean
clean:
rm -rf *.o *.so
有了动态库我们就可以gcc编译链接了,方法和静态库是一样的,还是
-
1.头文件和库都在标准路径下
-
2.库不放到标准路径下
-
3.库和头文件.h都不在标准路径下
在生成了可执行文件后直接./main执行会报错,ldd查看 会发现我们main找不到库的路径
但是我们gcc的时候不是给他指定路径了吗。
因为gcc是用来生成可执行文件的,我们给的路径也是生成可执行文件的时候才会被看到,现在已经到了执行可执行文件的时候了,操作系统会默认到lib/64链接需要的库,显然我们的库不在这里,那么我们就要再告诉OS这个库的路径了。
- 解决方法1.
把我们的动态库拷贝到lib64目录,可以cp,也可以软链接(记得用绝对路径)
- 解决方法2.
修改环境变量
系统会到LD_LIBRARY_PATH这个环境变量存储的路径下找库,我们把我们自己的库的路径加进来就好了
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/qingguo/project23_link/other/stdc/lib/
环境变量在退出shell后就重置了,为了保存效果我们可以去该用户的根目录下,先cd ~到用户的根目录,再找到.bashrc,
修改里面的LD_LIBRARY_PATH即可
此时就可以找到库的路径了
就可以运行了。
- 解决方法3
修改配置文件
在/etc路径下,有一个ld.so.conf.d的文件夹
我们在这里touch一个后缀为.conf的文件,在里面写入我们的库的路径(不带最后的库名称)即可
要用root,貌似sudo提权 也不好使,建议直接su
通过以上方法,我们就成功让库=文件找到了对应的库的路径
链接规则
现在我们的可执行文件包含四个库,三个是标准库ABC,一个是我们自己实现的库D,并且ABC库既有动态库也有静态库。
- 当D库动静态库都有的时候,默认gcc链接优先使用动态库链接ABCD
-
当D有静态库并且想使用静态库就加-static选项,此时链接ABCD都会用静态库
-
D只有动态库但是加了-static选项则会报错
-
D只有静态库即使没有指定-static,此时ABC会链接动态库,但是D会使用静态库