当前位置: 首页 > news >正文

Linux:软硬连接和动静态库

一般ll一下,最左边一列就是文件类型:

怎么创建链接文件:

ln -s 目标文件 创建的链接文件名

来试试:这叫软连接,软连接相当于Windows下的快捷方式,直接指向原文件的绝对路径;删除软连接不影响原文件

什么是硬链接?

创建硬链接

ln file_target2.txt file_hard.link

硬链接在Linux下相当于备份了一份对应文件

我们可以查看他们的inode来看他们的本质区别:

可以发现软连接的inode和原文件的inode是不一样的,但是硬链接的inode和原文件的inode是一样的

硬链接是通过文件的inode来引用原文件,软连接是通过文件的文件名来引用文件

为什么硬链接的inode和原文件的inode一样?

这个一样的inode其实起到的是一个映射的作用,建立一个新的硬链接就是代表了一串数字可以映射两个文件,指针指向同一个文件属性,指针计数为2

什么是指针计数?

这就是指针计数

我们可以看出:目录新建时都是两个指针计数,如果在目录内建立一个目录,计数+1,目录内再每有一个目录,计数-2

Linux下的.和..目录比较特殊,.表示当前目录,..表示上一级目录,这两个文件名是固定,所有系统和指令在使用时都预先知道他们的作用,你删除也是删除不了的(毕竟你无法删除你所在的目录)

硬链接一般都用作文件备份

打开的文件与内核、内存有关

没打开的文件和文件系统、磁盘有关

我们在向文件写入时可以使用两种方法:

int a=114514//二进制写入
114514 > "114514"//文本写入

动态库和静态库

我们之前在学习编译的过程的时候,第四步的库的链接就分为动态库和静态库

查看你的可执行程序对应的库:

ll查看一下我们的标准库

我们写一个c++的文件来看一下c++的标准库:

#include<iostream>
#include<string>
int main(){std::string name="hahaha";std::cout<<name<<std::cout endl;return 0;
}

查看依赖的库:

ldd 可执行程序文件名

后缀是.a的库是静态库,.so是动态库

愿意为你动态库是系统通过接口调用的,所以原则上是大家共用的

Windows的动态库的后缀是.dll和.lib

我们也可以通过file命令查看库的类型:

dynamically linked动态链接

回顾编译的四个步骤:

1.预处理:引入头文件,去注释,宏替换,条件编译。以#开头的属于预处理指令部分,例如#define,#if,#include,产生.i文件,指令:gcc -E mytest.c -o test.i

2.编译:完成语法和语义分析,生成汇编代码。此阶段可以检测到语义错误和语法错误,还有变量未定义的行为,指令:gcc -S test.i -o test.s

3.汇编:将汇编代码翻译为机器码指令,也就是二进制文件,文件格式是.o文件,指令:gcc -c test.s - o test.o

4.链接:完成文件中调用的函数跟静态库或动态库的链接,并将他们打包合并形成可执行文件:gcc test.o -o test

从上述四个过程中可以看出:只有.o文件,才涉及到库的事情,我们要使用自己制作的库或者链接别人的库,只要对这个.o文件链接就好了

一个可执行文件在运行前,里面的机器码由操作系统从磁盘上的动态库中复制到内存中,这个过程叫动态链接;操作系统采用虚拟内存机制允许物理内存中的动态库被其他进程使用,节省磁盘空间。

库的真实名字是:去掉前面lib前缀,去掉.a或.so后缀之后剩下的部分

库的制作和使用:

库是一个二进制文件,想使用库一定由三个部分组成:库文件,头文件,文档说明

库文件:函数的定义

头文件:函数的声明

我们来制作一个静态库:

//add.h
#pragma once
#include <stdio.h>
extern int my_add(int x, int y); 
//add.c
#include "add.h" 
int my_add(int x, int y) {return x + y;                                                 
}
//sub.h
#pragma once
#include <stdio.h>
extern int my_sub(int x, int y); 
//sub.c
#include "sub.h"                                                   
int my_sub(int x, int y){return x - y;
}

.h文件里的#pragma once的作用是防止该文件被多次引用,宏定义不会和其他定义冲突

#ifndef起到的作用和#pragma once 类似,但是前者的连内容相同的文件也可以不同时包含,缺点是宏定义如果有重复的时候也会被过滤掉

#indef是受c/c++语言标准库支持的,而#pragmat once 是编译器支持的,有的编译器肯不支持这样使用

1.静态库的制作:

我们得先有一个自己的.o文件:

然后我们要打包

使用我们的打包命令:

ar -rc libmymath.a add.o sub.o

ar是gnu的归档工具,将目标文件打包为静态库
-c(create):建立静态库文件

-r(replace):若静态库的目标文件有更新,则用新的替换旧的

ar命令也可以用来查看静态库中的文件:

ar -tv libmymath.a 

-t:列出静态库中的文件

-v:显示详细信息

发布静态库

我们的静态库要供人使用,除了.o文件以外,还要把头文件也发布出去:

创建一个output目录->把库文件和头文件一起放进去

cp -rf libmymath.a output/
cp *.h output/
*是一个通配符,上述命令的意思为复制所有以.h结尾的文件

上述的步骤我们也可以通过写一个makefile,来集成操作:

Makefile:
libmymath.a:sub.o add.oar -rc $@ $^%.o:%.cgcc -c $<.PHONY:output
output:mkdir outputcp -rf *.h outputcp libmymath.a output

我的已经是最新的所以up to date

静态库的使用:

方法一:

我们创建一个新目录:

mkdir friend

在里面写一个程序:

#include "add.h"
#include "sub.h"
int main() {int x = 30;int y = 20;int ret1 = my_add(x, y);int ret2 = my_sub(x, y);printf("ret1 = %d\n",ret1);printf("ret2 = %d\n",ret2);return 0;
}

来编译一下:

我们发现失败了,原因是缺少头文件add.h

因为编译器在编译时会在当前目录下的文件中寻找库,但是我们这个目录下只有mytest.c:

所以我们需要告诉编译器,库文件在那个目录下,需要给出路径:

gcc mytest.c -I /home/name/test

又报上错了。。为什么呢?

我们只告诉编译器头文件,还没告诉它库文件在哪

所以应该:gcc mytest.c -I /home/name/test -L /home/name/test -l mymath

格式是:

gcc 你的c程序 -I 头文件路径 -L 库文件路径 -l 库文件名称
  • -I(大写i):指定头文件所在路径。
  • -L:指定库文件所在路径。
  • -l(小写l):指明需要链接库文件路径下的哪一个库

这里也可以写成makefile:

mytest.out:mytest.cgcc -o $@ $^ -I /home/name/test -L /home/name/test -l mymath
.PHONY:clean
clean:rm -f mytest

方法二:将我们做的库放到系统默认的库的路径下,这样以前怎么编译,现在怎么编译

制作动态库:

gcc -shared -o libmymath.so add.o sub.o

-shared:Linux在gcc编译时加上-shared参数时,使源码编译成动态库文件.so

发布动态库:

我们可以把这些库文件头文件全部打包起来(其实静态库也需要这么干):

如果是makefile是这么写:

libmymath.so:add.o sub.ogcc -shared -o $@ $^
%.o:%.cgcc -fPIC -c $<
.PHONY:clean
clean:rm -rf libmymath.so *.o lib
.PHONY:lib
lib:mkdir libcp *.h libcp libmymath.so lib

动态库的使用:

和静态库一样但略有区别

 gcc mytest.c -I /home/name/test/lib -L /home/name/test/lib -l mymath

如果按静态库的使用方法你会发现能编译但不能运行

我们ldd一下发现库这里写的not found

为什么呢?

因为编译的时候是编译器负责,执行的时候是加载器完成,我们需要在运行的时候告诉系统库文件在哪里

1.把我们自己库文件拷贝到默认路径/usr/lib,这里不做演示

2.更改LD_LIBRARY_PATH

export LD_LIBRARY_PATH=/home/name/test/lib

LD_LIBRARY_PATH环境变量用于在程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径。注意,LD_LIBRARY_PATH中指定的路径会在系统默认路径之前进行查找

再次ldd查看:

欸,为什么还是不行?

别着急,更新一下缓存

 sudo ldconfig /home/name/test/lib

大概就是这样啦

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • PhotoZoom9怎么样?图片模糊怎么办?
  • navigator.mediaDevices.getUserMedia检查用户的摄像头是否可用,虚拟摄像头问题
  • 基于MinerU的PDF解析API
  • AUC真的什么情形下都适合吗
  • COD论文笔记 BiRefNet
  • Spark MLlib模型训练—聚类算法 PIC(Power Iteration Clustering)
  • Python | Leetcode Python题解之第386题字典序排数
  • 图文解析保姆级教程:Postman专业接口测试工具的安装和基本使用
  • ChatGPT 3.5/4.0使用手册:解锁人工智能的无限潜能
  • nginx配置负载均衡的几种方式
  • 快手的视频素材去哪找?快手视频素材在哪里找啊
  • 【排序算法】六、快速排序补充:三指针+随机数法
  • Bat的退役前
  • 0基础学习爬虫系列:Python环境搭建
  • Java 中的双冒号“::”
  • hexo+github搭建个人博客
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • egg(89)--egg之redis的发布和订阅
  • es6要点
  • Less 日常用法
  • MyEclipse 8.0 GA 搭建 Struts2 + Spring2 + Hibernate3 (测试)
  • mysql_config not found
  • Redis 中的布隆过滤器
  • vue数据传递--我有特殊的实现技巧
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 类orAPI - 收藏集 - 掘金
  • 容器服务kubernetes弹性伸缩高级用法
  • 深入浏览器事件循环的本质
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 学习Vue.js的五个小例子
  • 一个JAVA程序员成长之路分享
  • Oracle Portal 11g Diagnostics using Remote Diagnostic Agent (RDA) [ID 1059805.
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • 翻译 | The Principles of OOD 面向对象设计原则
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • #{}和${}的区别?
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • %@ page import=%的用法
  • (11)MSP430F5529 定时器B
  • (17)Hive ——MR任务的map与reduce个数由什么决定?
  • (4)logging(日志模块)
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (阿里云万网)-域名注册购买实名流程
  • (不用互三)AI绘画工具应该如何选择
  • (第30天)二叉树阶段总结
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (原創) 人會胖會瘦,都是自我要求的結果 (日記)
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转)拼包函数及网络封包的异常处理(含代码)
  • (转)使用VMware vSphere标准交换机设置网络连接
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .net MySql
  • .NET/C# 解压 Zip 文件时出现异常:System.IO.InvalidDataException: 找不到中央目录结尾记录。
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景