前提知识
当我们的文件没有被打开的时候,只能在磁盘放着,磁盘上有大量的文件,这么多文件,也是要被静态管理起来的,方便我们随时打开。
1.1.磁盘的物理结构
1.磁盘是我们计算机中唯一的一个机械结构。
2.磁盘是一个机械结构,而且还是一个外设,所以相比一CPU他就很慢很慢。
3.现在我们使用的笔记本电脑,已经很少用磁盘(HDD)了。用的是固态硬盘(SSD)。
4.在企业端,磁盘依旧是存储的主流。一般是SSD和HDD混和使用。
![](https://i-blog.csdnimg.cn/blog_migrate/ed5646b0d0f3b5e33ef53fa584d5e6a9.png)
因为磁头和盘面的的距离很近很近,所以磁盘要防止抖动和防止灰尘。所以磁盘的密封性能一定很好。
1.2.磁盘的存储结构
![](https://i-blog.csdnimg.cn/blog_migrate/17139724252e9a23874877ad5f360f16.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c0c091a06e354691f5184b3cec8c82e0.png)
1.3.磁盘的逻辑结构
1.3.1.OS对磁盘的方法单位
![](https://i-blog.csdnimg.cn/blog_migrate/3dcfeb433728043bbb03e5176f6ab08e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bfb1b4011caee43c66e73dcc680c7517.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3c9098a85265563f74360e081214e500.png)
1.3.2.OS对磁盘IO过程的优化
![](https://i-blog.csdnimg.cn/blog_migrate/fe35c14b67e19c21a5609e06021d69ca.png)
可以stat “文件名”:查看文件具体信息
![](https://i-blog.csdnimg.cn/blog_migrate/a5b739241ef01c8be28f551b7a4f78c1.png)
文件系统
2.1.前提知识
![](https://i-blog.csdnimg.cn/blog_migrate/11ad39ed08426519cb6d8898a04e3997.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cedf8e5893a0039c1df394774f793a3a.png)
对文件属性和文件内容的认识:
![](https://i-blog.csdnimg.cn/blog_migrate/699e6ebec18c8cd94ad986fca4002c8c.png)
2.2.Linux文件系统
![](https://i-blog.csdnimg.cn/blog_migrate/f0c5078c78db8e28c3427c0138e153d3.png)
Linux ext2文件系统,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的block。一个block的大小是由格式化的时候确定的,并且不可以更改。例如mke2fs的-b选项可以设定block大小为1024、2048或4096字节。而上图中启动块(Boot Block)的大小是确定的。
Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。政府管理各区的例子
超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏(一般有备份),可以说整个文件系统结构就被破坏了。
GDT,Group Descriptor Table:块组描述符,描述块组属性信息,有兴趣的同学可以在了解一下
块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用
inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
inode节点表(inode table):存放文件属性 如 文件大小,所有者,最近修改时间等
数据区(data blocks):存放文件内容
磁盘格式化就是重新写入文件系统。
2.3.读取工作过程
![](https://i-blog.csdnimg.cn/blog_migrate/9c5b1fc80926d63b23134122b866dc7c.png)
1.先获得文件inode--》 2.通过inode信息获得数据块的下标---》3.完成读取操作
2.4.目录文件
![](https://i-blog.csdnimg.cn/blog_migrate/e06281cd98380f31011618855d19a57c.png)
2.5.添加文件
1. 存储属性
内核先找到一个空闲的i节点(这里是263466)。内核把文件信息记录到其中。
2. 存储数据
该文件需要存储在n个数据块,内核找到了n个空闲块:300,500,800 --- 。将内核缓冲区的第一块数据复制到300,下一块复制到500,以此类推。
3. 记录分配情况
文件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。
4. 添加文件名到目录(在目录中建立文件名和文件inode的映射关系)
新的文件名test.txt。内核将入口(263466,test.txt)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来。
2.6.删除文件
删除文件很简单,只需要修改GDT(块组描述符)。数据块对应的位图结构(block bitmap)和inode对应的位图结构(inode bitmap)即可。其实不是真正意义删除,只是把原有的数据(inode和数据块)从开始的不可覆盖,变成了覆盖,不受保护了。
所以按理说我们删除文件后,知道删除文件的inode (通过日志信息)。只要那一块内存没有被覆盖 是有可能恢复的。
硬链接 和 软链接
3.1.硬链接
介绍
我们看到,真正找到磁盘上文件的并不是文件名,而是inode。其实在linux中可以让多个文件名对应于同一个inode。
touch test.txt :创建一个文件
ln test.txt hard_test.txt :给test.txt创建一个硬链接
ls -1i:查看文件对应的inode
![](https://i-blog.csdnimg.cn/blog_migrate/d38807b9c811c88526ecb15b0b49087c.png)
这不就是给inode起一个别名嘛。
用法
![](https://i-blog.csdnimg.cn/blog_migrate/3243b0782c04fdabf44b33ea227b6ec0.png)
也可以看看文件的inode
![](https://i-blog.csdnimg.cn/blog_migrate/df93da697b9e19252a67b3d955bdbeaf.png)
所以每一个目录下的 "." 和 ".." 就是硬链接。
这也就解释了为什么 'cd ..' 能回到上级目录。
特例
"." 和 ".." 就是硬链接,为什么我们不能给目录建立硬链接?
![](https://i-blog.csdnimg.cn/blog_migrate/7b74ecacc74dffb4e76e9d2f761850b3.png)
假如我们给根目录建立一个硬连接,当我们从根目录去查找某一个文件的时候,可能就会生成一个闭环,死循环了。
3.2.软链接
介绍
硬链接是通过inode引用另外一个文件,软链接是通过名字引用另外一个文件,所以软连接标定一个文件不是通过文件的inode编号,而是通过文件名称。
ln -s test.txt soft_test.link :给test.txt创建软链接
![](https://i-blog.csdnimg.cn/blog_migrate/4b49a4191455d61e3392571823f5e7fc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/32a89daaf2925bed128e496c7a465d29.png)
这不就是win下的快捷方式嘛。
用法
指向别的可执行程序。(快捷方式)
![](https://i-blog.csdnimg.cn/blog_migrate/302d078c7f57c94a96a2fc02afc87c6f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6e7a81655c6d519b5ab3f4ffb97b5098.png)
3.3.ACM时间
![](https://i-blog.csdnimg.cn/blog_migrate/7c8e4cd8612cfd7cd614ee3b19c2a81d.png)
动态库和静态库
4.1.特征介绍
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间
先来一段测试代码:
![](https://i-blog.csdnimg.cn/blog_migrate/61538b26f1b75027435c67dda9c8cb75.png)
一起我们写项目的时候,都会这样使用,在main.c包含各自运用到函数的头文件,然后全部的“xxx.c"文件一起编译,即可编译完成。
![](https://i-blog.csdnimg.cn/blog_migrate/015fad20fa40c0e52ebf2054d0f2c72f.png)
这是我们的以前学到的,头文件和源文件基本使用。、
也可以分开编译最后再链接
![](https://i-blog.csdnimg.cn/blog_migrate/78b8f8430ce828581741a841925e7741.png)
![](https://i-blog.csdnimg.cn/blog_migrate/24ac8925001b4e8069ef1af89052ac3f.png)
可以看到一样可以形成可执行文件。
![](https://i-blog.csdnimg.cn/blog_migrate/cd8a3c36e831a4bd51eba4456b28a2ea.png)
![](https://i-blog.csdnimg.cn/blog_migrate/67deeb8865fdb25e324d8e73220cfc90.png)
所以我们只给别人提供可重定向目标二进制文件和对应的头文件,就可以给别人提供我们的函数使用。
这种思想就已经是库的思想了。
将所以的“xxx.o”文件打包,形成库,提供给对方一个库文件和全部的头文件即可。
打包就是将多个“xxx.o”文件合起来形成一个文件,这就是库文件。
根据打包方法的不同,分为静态库和动态库。
4.2.生成静态库
ar -rc 【生成的库名称】 【全部的“xxx.o”文件】
//ar是gnu归档工具,rc表示(replace and create)(替换并创建)
直接创建一个makefile自动化文件。
![](https://i-blog.csdnimg.cn/blog_migrate/1823b130f672f794d10407d9dfb472be.png)
![](https://i-blog.csdnimg.cn/blog_migrate/60d43c1b3257e79e0fafa0e4cfc4369e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/900ba2121bcb05c4009a4135d96399f4.png)
查看生成静态库:
ar -tv 【库文件】:查看库文件是由那俩个文件
t:列出静态库中的文件
v:verbose 详细信息
![](https://i-blog.csdnimg.cn/blog_migrate/43a0dbfdc299659d148cc6935b1cbf4b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3a2db640a60f8475bb1c2b097038c236.png)
交付库的时候:
1.提供:“xxx.a”/“xxx.so”文件;
2.提供对应匹配的 “xxx.h”文件。
一般都会打包好到一个目录,然后再压缩给别人。
4.3.使用静态库
![](https://i-blog.csdnimg.cn/blog_migrate/070ebc7333deb9f383af7d83d409b1f1.png)
安装的本质就是:把文件拷贝到知道系统能找到的目录下。
方法一:直接使用
![](https://i-blog.csdnimg.cn/blog_migrate/eebf0f1a99590461accab2232adf1ad8.png)
gcc的链接规则:
![](https://i-blog.csdnimg.cn/blog_migrate/62575f99c2437642f02f47a27738fba5.png)
1,gcc默认是动态链接(建议行为),对于特定的库,究竟是动态还是静态,取决于你提供的是动态库还是静态库。有动态库就动态链接。
2,有多个库的时候,一个一个链接的,可能一部分是动态链接,一部分是静态链接,只要有一个库是动态链接的,那里就会显示是动态链接。
方法二:安装到系统对应路劲下
![](https://i-blog.csdnimg.cn/blog_migrate/488f29a4b77bc8187496c6eec9f33c76.png)
建议测试完后立即卸载掉,我们自己写的程序放在系统的路劲下不安全。
卸载:就是把刚刚拷贝到系统默认路劲下的文件原封不动的删除掉。一定注意不要删错了。
4.4.生成动态库
![](https://i-blog.csdnimg.cn/blog_migrate/66d269aa40d0d8cc499b0af00295ecc2.png)
交付库的时候:
1.提供:“xxx.a”/“xxx.so”文件;
2.提供对应匹配的 “xxx.h”文件。
一般会打包好到一个目录下,然后压缩好给别人
![](https://i-blog.csdnimg.cn/blog_migrate/bdc6148153f35612d679bc1d8f28decd.png)
4.5.使用动态库
不能像静态链接那样世界使用,运行会找不到对应的动态库。
![](https://i-blog.csdnimg.cn/blog_migrate/9e276e3b8b98f4b432b0a707b5ecd715.png)
库的默认搜索路劲
1, 环境变量LD_LIBRARY_PATH
2, /usr/lib/(32位)或/usr/lib64/(64位)
3, /usr/local/lib/(32位) 或 /usr/local/lib64/(64位) (这个是用户级别的)
4, /lib64/
方法一:修改LD_LIBRARY_PATH环境变量
![](https://i-blog.csdnimg.cn/blog_migrate/7740db324a256c4a99e165f9f020d2da.png)
但是环境变量是内存级的,当我们重新链接我们的机器的时候他就不能再次执行了。因为环境变量改变了。如果像使环境变量不变就需要去修改配置文件了,比较麻烦。临时做测试可以。
方法二:安装进系统
![](https://i-blog.csdnimg.cn/blog_migrate/5804442dcf30911a8b7bf184d83ce12a.png)
方法三:配置文件(xxx.conf)
在 /etc/ld.so.conf.d/ 路劲下有很多的配置文件
![](https://i-blog.csdnimg.cn/blog_migrate/834778fd3a07384d977be275fc235ca3.png)
动态库在进行搜索时,包含路劲的配置文件。
创建一个任意名称的xxx.conf 文件
![](https://i-blog.csdnimg.cn/blog_migrate/c615710accde541999fc8190a89ab2e0.png)
给刚刚创建的配置文件写入,我们的动态库所在的目录(sudo添加)
![](https://i-blog.csdnimg.cn/blog_migrate/e15b8758f988a617e8e392b19664ae75.png)
然后更新一下:sudo ldconfig(一定要做)
![](https://i-blog.csdnimg.cn/blog_migrate/8612df428af6c2aa9c6971dc64b83d9d.png)
这个和安装到系统一样的效果,再次链接还能使用。
删除配置文件的时候也要ldconfig
方法四:创建软连接
在当前目录下建立软连接
![](https://i-blog.csdnimg.cn/blog_migrate/1fdaa6780ff1e051f5528280793f991d.png)
在系统目录下建立软连接
![](https://i-blog.csdnimg.cn/blog_migrate/24f0751f5d45300ec97682f47f60b678.png)
总结
我们能让可执行找到动态库文件的时候,我们再把头文件拷贝进系统,这样就只需指定 ”-l 库名“,即可完成编译任务 。运行时候不需要库文件,但是需要用到动态库(而且还要能找到)。
![](https://i-blog.csdnimg.cn/blog_migrate/5ceec48fc9e912070fb59fe268afca0b.png)
动静态库的加载
5.1.静态库
静态库一般不考虑加载过程
因为静态链接编译的时候,就已经将对应的实现加载到了可执行程序你内部,也就是说静态库,在编译完成后,就不需要库了。可执行程序需要的代码,已经被拷贝进去了。
会有代码冗余的现象:例如:每次调用printf,都需要加载一份priintf的实现。
编译是加载,到哪里呢?加载进了代码区。(建议复习进程地址空间)
拷贝进来的代码,必须通过相对确定的地址位置来进行访问。
拷贝进代码区,这里也就和我写的函数一样。采用绝对编址的方式。
5.2.动态库
静态链接:编译的时候在调用函数的位置完成实现代码的拷贝。(代码冗余)
动态链接:编译的时候在调用函数位置放入偏移量地址。
动态库是在使用的时候被加载进内存,然后映射到进程的共享区,确定start地址,然后在可执行程序中存放的是偏移量地址。start地址+偏移量地址确定虚拟地址,通过页表映射完成执行。
![](https://i-blog.csdnimg.cn/blog_migrate/7d78aebebe7908a5e0e060cb2e23b1bb.png)