【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会使用静态库

相关推荐
程序员小崔日记11 小时前
20块钱注册一年 .COM 域名!Spaceship 最新优惠教程,支付宝即可付款(附优惠码)
服务器·腾讯云·域名
Better Bench11 小时前
WSL2 Ubuntu 中 Claude CLI “command not found” 故障排查与修复
linux·ubuntu·claude·wsl·claudecode
实心儿儿12 小时前
Linux —— 进程间关系和守护进程
linux·运维·服务器
FII工业富联科技服务12 小时前
工业设备运维如何Agentic化?拆解Factory Brain的设备运维架构
大数据·运维·人工智能·架构·制造
林浅不想努力12 小时前
LVS知识点总结
服务器·云原生·lvs
石小石Orz12 小时前
我发现了开发者AI产品营收的新方向
前端·虚拟现实
老马识途2.013 小时前
关于跨域问题的总结
java·前端
Dawn-bit13 小时前
Linux磁盘管理详解
linux·运维·服务器·计算机网络·云计算
RisunJan14 小时前
Linux命令-sftp(SSH 文件传输协议客户端)
linux·运维
别惊醒渔人14 小时前
Vue3 Diff 优化:最长递增子序列 LIS
前端·javascript·vue.js