【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 charfilename,const charmode){
    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,mFILEstream){
    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(mFILEstream){
    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 charfilename,const charmode);
    int mfwrite(charstr,int num,mFILEstream);
    void mfflush(mFILEstream);
    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文件]
  1. ar事archive的缩写,意思是把。。归档
  2. -r表示repalce,即若库中已有同名文件则进行替代
  3. -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中的printfscanf等。为了方便开发者,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库既有动态库也有静态库。

  1. 当D库动静态库都有的时候,默认gcc链接优先使用动态库链接ABCD

  2. 当D有静态库并且想使用静态库就加-static选项,此时链接ABCD都会用静态库

  3. D只有动态库但是加了-static选项则会报错

  4. D只有静态库即使没有指定-static,此时ABC会链接动态库,但是D会使用静态库

相关推荐
长流小哥4 分钟前
STM32 ADC+DMA+TIM触发采样实战:避坑指南与源码解析
stm32·单片机·嵌入式硬件·keil5
道亦无名13 分钟前
STM32控制电机
stm32·单片机·嵌入式硬件
云攀登者-望正茂15 分钟前
无缝部署您的应用程序:将 Jenkins Pipelines 与 ArgoCD 集成
运维·jenkins·argocd
wingaso1 小时前
[经验总结]删除gitlab仓库分支报错:错误:无法推送一些引用到“http:”
linux·数据仓库·git
独行soc1 小时前
2025年渗透测试面试题总结-阿里云[实习]阿里云安全-安全工程师(题目+回答)
linux·经验分享·安全·阿里云·面试·职场和发展·云计算
happygrilclh1 小时前
STM32的ADC模块中,**采样时机(Sampling Time)**和**转换时机(Conversion Time),获取数据的时机详解
stm32·单片机·嵌入式硬件
勤不了一点1 小时前
小白上手RPM包制作
linux·运维·服务器·软件工程
waterHBO1 小时前
直接从图片生成 html
前端·javascript·html
盛夏绽放2 小时前
Python字符串常用内置函数详解
服务器·开发语言·python
互联网搬砖老肖2 小时前
React组件(一):生命周期
前端·javascript·react.js