• 【Linux】文件系统及动静态库


    目录

    一、文件系统

    1、基本知识

    2、磁盘文件

    3、磁盘结构(磁盘与OS的映射关系)

    (1)物理结构

    (2)磁盘的抽象

    (3)、深入理解inode

    4、软硬链接

    二、动静态库

    1、静态库

    (1)、静态库的制作

    (2)、静态库的使用

    2、动态库

    (1)、动态库的制作

    (2)、动态库的使用

    3、使用库的原因

    总结


    一、文件系统

    1、基本知识

    前面我们所提及的文件都是被打开的文件,是被加载到内存的文件,当然操作系统中存在大量没有被打开的文件,它们主要储存在磁盘中(磁盘级文件)

    我们主要侧重点在于

    单个文件:这个文件在哪里?文件有多大?文件的其它属性是什么?

    站在系统角度:有多少文件?各自的属性在哪里?如何快速找到?我还可以存储多少文件?如何快速找到指定文件

    2、磁盘文件

    计算机存储介质主要分为:

    掉电易失存储介质和永久性存储介质

    内存:掉电易失存储介质

    磁盘:永久性存储介质   SSD,U盘,flash卡,光盘,磁带

    磁盘作为一个外设且是计算机中唯一一个机械设备,它是十分的慢的,所以OS一定会有一些提速的方式

    3、磁盘结构(磁盘与OS的映射关系)

    (1)物理结构

    磁盘主要是由磁盘盘片,磁头,伺服系统,音圈马达等构成

    磁盘是在它的盘面上存储数据-》磁盘是有磁性的(N,S极)正好对应了计算机的二进制

    对磁盘进行写入就是改变磁盘上的极性

    并且磁盘是由一摞盘面构成,每个盘面都会有磁头

    磁盘储存数据的基本单位是512字节

    每一个扇区能够存储的数据大小一般都是512字节,通过控制磁盘密度不同使扇区物理大小不同,而存储相同大小的数据

    在物理层面上,如何把数据写入到指定的扇区里?

    CHS寻址方式:

    首先要找到数据在哪个盘面上存储

    接下啦寻找在哪个磁道(柱面)上

    最后确定在哪个扇区

     CHS寻址模式将硬盘划分为磁头(Heads)、柱面(Cylinder)、扇区(Sector)。

    磁头(Heads):每张磁片的正反两面各有一个磁头,一个磁头对应一张磁片的一个面。因此,用第几磁 头就可以表示数据在哪个磁面。

    柱面(Cylinder):所有磁片中半径相同的同心磁道构成“柱面",意思是这一系列的磁道垂直叠在一起,就形成一个柱面的形状。简单地理解,柱面数=磁道数。

    扇区(Sector):将磁道划分为若干个小的区段,就是扇区。虽然很小,但实际是一个扇子的形状,故称为扇区。每个扇区的容量为512字节。

     知道了磁头数、柱面数、扇区数,就可以很容易地确定数据保存在硬盘的哪个位置。也很容易确定硬盘的容量,其计算公式是: 硬盘容量=磁头数×柱面数×扇区数×512字节  

    (2)磁盘的抽象

    磁盘是圆形结构-》我们可以将其按照一个剖面展开,抽象出线性结构

    将一个大磁盘抽象出一个大数组,然后操作系统对磁盘的管理转变为对数组的管理

    将数据存储到磁盘-》将数据存储到数组

    找到磁盘特定扇区的位置-》找到数组特定位置

    对磁盘的管理-》对该数组的管理

    因为磁盘十分的大,我们需要将其划分为一小块,一小块的空间,然后管理——分区

     磁盘的基本单位是扇区(512字节),但是操作系统和磁盘进行I/O的基本单位是4kb

    为什么操作系统不以512字节为单位呢?

    (1)、太小了,有可能需要多次I/O,进而导致效率降低

    (2)、如果操作系统使用和磁盘一样的大小,如果磁盘的大小改变,操作系统的源代码也需要进行修改,使用4kb实现了操作系统和硬件的解耦合

    Data blocks:多个4kb空间的集合,保存的都是特定的文件内容

    inode :inode是一个大小为128字节的空间,保存的是对应文件的属性,包含自己的inode编号

    这也就说明Linux下,文件的内容和属性是分开存储的

    inode Table:该块组内所有文件的inode空间的集合,需要唯一标识,每一个inode块都需要有一个inode编号,简言之:一般来说一个文件一个inode编号

    BlockBitmap:假设有1W+个blocks,就会有1W+个比特位,比特位与特定的blocks是一一对应的,其中比特位为1,代表该blocks被占用,否则表示为可用

    inodeBitmap:假设有1W+个inode,就会有1W+个比特位,比特位与特定的indoe是一一对应的,其中比特位为1,代表该inode被占用,否则表示为可用

    GDT:快组描述符,这个块组的大小,已经使用多少空间,有多少个inode,已经占用多少了

    还剩多少,一共有多少block,还剩多少……

    块组分割成为上面的内容,并且写入相关的管理数据-》每一个块组都这么干-》整个分区就被写入了文件系统


    一个文件只对应一个inode属性节点-》有唯一的inode编号

    一个文件可以有多个block、

    inode中存有与该文件映射的block的块号,如果文件特别大,有的block里面就会存储该文件的相关的块号来实现存储大量文件


    (3)、深入理解inode

    inode编号是当前分区有效

    找到文件:inode编号-》分区特定的blockgroups-》inode-》属性-》内容

    在Linux中inode属性中没有文件名

    一个目录下,可以保存很多文件,但是这些文件名是不允许重复的

    目录也是一个文件,也要有inode,也要有data blocks

    它的data blocks内部存放的是文件名和对应inode的映射关系,两者都是唯一确定的

    创建一个文件,系统做了什么?

    先在特定分区,特定块组中遍历inodeBitmap,找到为0的比特位,将其置为1,inode中写入它的属性,建立与data blocks的映射关系,清空特定的data blocks,建立文件名与inode的映射关系,其中文件名从用户中来,inode从系统中来

    文件创建失败的原因

    inode和data blocks都是固定的,可能inode没有了,data block还存在

    inode有,data block没有了

    4、软硬链接

    我们先创建一个文件

    然后与另一个文件建立软链接

     ls -s 文件名 链接文件名

    后一个链接前一个

    我们再硬链接test.txt

    这时我们发现soft.link和test.txt的inode是不同的,证明软链接是有独立inode的

    硬链接是没有独立inode的,它的inode与链接文件inode相同

    软硬连接本质区别:有无独立inode

    软链接有独立inode-》软链接是一个独立的文件

    硬链接没有独立inode-》硬链接不是一个独立文件


    软连接相当于创建了一个新文件,文件中存储的是指向某个文件的路径

    创建硬链接不是创建了一个真正的文件,硬链接用的是其它文件的属性和数据

    硬连接的本质是在指定路径下建立了文件名与inode的映射关系

    一个inode可以映射多个文件名,删除其中一个文件,相当于去除与inode的映射关系,只有将该文件的全部映射关系去掉,才意味着真正删除该文件

    默认创建目录,引用计数(硬链接数)为什么是2?

    这是因为 自己目录名 与inode建立映射

    目录内部还会有 . 记录当前目录


    如果我们想要去除链接关系可以使用unlink命令

    二、动静态库

    对于Linux下静态库后缀.a  动态库.so

    库中是没有main函数的

    并且需要将.c/.cpp文件编译成.o文件

    如果我们只把.h和.o文件给别人,别人是能够直接使用的

    同时我们所写的库都是第三方库,写好后直接编译是不能通过的

    Linux下头文件gcc的默认搜索路径是:/usr/include

    库文件的默认搜索路径是:/lib64  or /usr/lib64

    我们将库拷贝到系统的默认路径下,叫做库的安装

    所以我们要使用库,无论是静态库还是动态库都需要我们手动指明库的位置,头文件的位置

    1、静态库

    (1)、静态库的制作

    1. //myprint.h
    2. #include
    3. extern void print();
    4. //myprint.cpp
    5. #include "myprint.h"
    6. void print()
    7. {
    8. std::cout << " Hello World" << std::endl;
    9. }
    10. //mymath.h
    11. #include
    12. extern int GetSum(int left, int right);
    13. //mymath.cpp
    14. #include "mymath.h"
    15. int GetSum(int left, int right)
    16. {
    17. int ans = 0;
    18. for(int i = left; i <= right; i++)
    19. {
    20. ans += i;
    21. }
    22. return ans;
    23. }

    按照前面所说我们需要将.c/.cpp编译为.o文件

    然后使用ar命令打包

    ar -rc libhello.a mymath.o myprint.o

    这就形成了静态库,这个过程我们也可以使用makefile来进行简化

    1. //makefile
    2. libhello.a: myprint.o mymath.o
    3. ar -rc libhello.a mymath.o myprint.o
    4. mymath.o: mymath.c
    5. gcc -c mymath.c -o mymath.o -std=c99
    6. myprint.o: myprint.c
    7. gcc -c myprint.c -o myprint.o -std=c99
    8. .PHONY:hello
    9. hello:
    10. mkdir -p hello/include
    11. mkdir -p hello/lib
    12. cp -rf *.a hello/lib
    13. cp -rf *.h hello/include
    14. .PHONY:clean
    15. clean:
    16. rm -rf *.o libhello.a

    (2)、静态库的使用

    静态库的使用这里只说明不修改系统的方法

    gcc -o test.exe main.c -I hello/include -L hello/lib -lhello
    

     -I 代表指明头文件路径

    -L代表库所在的路径

    -l代表该路径下的具体库的名字

    库的真实名字:去掉前缀lib 去掉后缀.a或者.so

    2、动态库

    动态库的制作和静态库类似,不过是在编译时增加了新的选项

    (1)、动态库的制作

    同样道理,制作动态库也需要我们将文件编译成为.o不过要加上-fPIC选项

    fPIC:产生位置无关码(position independent code)
    gcc -fPIC -c mymath.c -o mymath.o

    编译成.o文件之后的操作是打包

    gcc -shared myprint.o mymath.o -o liboutput.so

    同样我们也可以写成makefile

    1. liboutput.so: myprint.o mymath.o
    2. g++ -shared mymath.o myprint.o -o liboutput.so
    3. mymath.o:mymath.cpp
    4. g++ -fPIC -c mymath.cpp -o mymath.o
    5. myprint.o:myprint.cpp
    6. g++ -fPIC -c myprint.cpp -o myprint.o
    7. .PHONY:output
    8. output:
    9. mkdir -p output/include
    10. mkdir -p output/lib
    11. cp *.h output/include
    12. cp *.so output/lib
    13. .PHONY:clean
    14. clean:
    15. rm -rf output *.o *.so

    (2)、动态库的使用

    a.如果我们只有静态库,没办法,编译器只能针对该库进行静态链接

    b.如果动静态库同时存在,默认使用动态库

    c.如果动静态库都存在,非要使用静态库 使用-static选项,摒弃默认优先使用动态库的原则,直接使用静态库的方案

    当我的程序要使用动态库中的代码时,将动态库加载到内存,并且与页表进行映射,映射动态库代码到共享区


    静态库使用绝对地址的方案,必须从全局的角度看待,因为它将库与可执行程序一同加载

    动态库是独立的库文件,可以分批加载,随意加载到任意位置,然后与页表进行映射

     然后运行

    报错,找不到动态库。

    可是我们前面已经声明了动态库的位置,这里为什么还是找不到呢?

    因为我们声明动态库位置是对编译器的,而编译完成后就与编译器没有任何关系了

    所以我们需要对操作系统声明动态库的位置 

    我们可以建立一个库的软连接文件到/lib64目录中

     

     

    然后运行

     

    3、使用库的原因

     

    站在使用库的角度,库的存在,可以大大减少我们的开发周期,提高软件的质量

    站在写库的角度:1、简单,2、安全

    库是编译好的,并不会暴露我的源代码


    总结


    以上就是今天要讲的内容,本文仅仅是简单介绍了Linux文件系统及动静态库的制作和使用

  • 相关阅读:
    Go十大常见错误第9篇:使用文件名称作为函数输入
    typescript87-react中状态和事件
    【自用总结】正项级数审敛法的总结
    ERROR: your rosdep installation has not been initialized yet
    使用百度EasyDL实现钢筋计数
    在springboot下将mybatis升级为mybatis-plus
    角色认知的理解
    uniapp框架UI蓝图
    测开 - 自动化测试selenium(WebDriver API) - 细节狂魔
    【C++入门指南】C如何过渡到C++?祖师爷究竟对C++做了什么?
  • 原文地址:https://blog.csdn.net/m0_62179366/article/details/127719967