Linux(5)(下)

https://blog.csdn.net/qscftqwe/article/details/156130571

这是上节课链接,大家可以选择性看一下

一.缓冲区的概念

1. 缓冲区的分类

  • **内核缓冲区:**操作系统内核管理

  • **用户缓冲区:**用户程序/标准库管理

2.现象分析

为什么这两个显示不出来 ,因为这两个是写到用户缓冲区,而close关闭的是进程连接的文件系统,而这个文件系统连接的是内核缓冲区,当close后进程和该文件系统断开连接,因此进程无法完成把数据弄到内核缓冲区,因此程序退出后无法显示。

那为什么这两个可以显示出来呢,因为显示屏的刷新是行刷新,当有换行符触发了刷新从而用户缓冲区就将数据写到内核缓冲区因此程序退出后可以显示,因为目前认为只要数据到了内核,那么数据就可以找到硬件。

3.知识延申

这也是为什么有时候用_exit有些数据会丢失,实际是-exit只处理内核缓冲区的内容,因此程序退出后看到有些数据没有显示,而这些数据其实是在用户缓冲区。

4.缓冲区刷新种类

  • 无缓冲------直接刷新

  • 行缓存------不刷新,直到碰到\n(显示屏)

  • 全缓冲------满了才刷新(普通文件)

  • 程序退出后自动刷新

5.缓冲区的用处

  • 解决用户效率问题

  • 配合格式化

6.缓冲区在哪

缓冲区是被FILE结构体所维护,因此当有多个缓冲区那么相对于就有多少个FILE(属于用户),就有多少个文件描述符,

为什么这个代码会打印3行(C代码打两行,操作系统打一行),因为fprintf采用全刷新,而fork创建子进程的时候同样获得了用户缓冲区,到最后程序退出后自动刷新也就出现了代码执行了两次。

二.理解文件系统

1. 了解知识

2. 认识磁盘(硬件)

  • 所有硬件都只认识二进制,因为数字电子技术的物理特性。

  • 磁盘被访问的基本单元是扇区(512B或4KB),因此我们可以把磁盘看成无数个扇区构成的

  • 数据到硬盘分三步(CHS寻址方式):定位到那个磁头,定位到哪一个磁道,定位到哪一个扇区,因此如果想提高效率,在软件设计上应该把相关数据放在一起

    3.抽象理解磁盘

  • 把磁盘想成磁带(可拉的)然后想象成拉成一条直线,然后逻辑上是线性的那么我们就可以用下标来表示

  • 当我们用下标表示:磁头、磁道、扇区那么这个地址叫做LBA

根据这些步骤那么理解了CPU如何访问磁盘的数据

4.文件系统
因为完成了对磁盘的建模,因此对于磁盘我们是不是可以主观上看成多个小磁盘组成的,我们只要管理一个小磁盘然后将其管理方式到其他小磁盘上即可。

但是小磁盘依旧很大,那么可以继续划分
我们把一个小磁盘分成多个Block group,而我们只要管理好一个Block group即可,所以我们研究Block group有什么即可,而这个就叫文件系统。

5.文件系统的了解

  1. **Data blocks:**存放文件内容的区域,以块的方式呈现,常见的是4KB大小(一般一个数据一个块)

  2. inode Table: 文件的所有属性,128字节(一般一个文件一个inode,inode是唯一标识,文件属性不包括文件名称只使用inode

  3. **Block Bitmap:**用位图和Data blocks元素进行映射,用来表示该块有没有被使用,因此删除文件本质是把,位图标记的那块变成0

  4. **inode Table:**和Block Bitmap功能相同

  5. **Group Descriptor Table:**整个分组的使用情况

  6. Super Block:存放文件系统基本信息:包含每个分区的基本使用情况,如一共有多少个组,每组大小,每组inode和block数量,每个组的起始inode值,文件系统类型与名称......,它不是每个组都存在,但是存在多个(避免挂掉一个,一个分区全挂了)

6. 格式化 ​​​和​ 对inode深刻了解​​

  • 每一个分区被使用之前,都必须提前将部分文件系统的属性信息导入对应的分区中,而其中的inode和Block位图一开始是空的,因此格式化会把原先的所有内容清空。

  • 一个文件一个inode每个inode都有唯一编号(inode的设置是以分区为单位的,不能跨分区)

  • 怎么知道一个文件inode编号,为什么我关心的是文件名而不是inode

  • 理解上面那个问题得先理解目录: 目录也是文件那么也应该具备自己的inode,因此目录的数据块存放的是文件名和inode的映射关系 ,这也反映了为什么同一目录下面不能有相同的文件名。因此就满足了怎么知道一个文件的inode和为什么关心的是文件名而不是inode。

  • 但是目录的inode是如何知晓的呢,其实目录是通过递归方式得知的,因为所有目录要么是根目录的孩子或者间接的孩子 ,但是这样如果每次访问文件都要一步步找根目录不是很麻烦,因此dentry就是用来缓存经常使用的目录存放它们的inode

7. 软硬链接

**软链接:**是一个独立文件具有独立的inode,它的数据块存放的是指向文件的路径(相当于window的快捷方式)

软链接使用:

bash 复制代码
ln -s 文件路径 连接路径
ln -s ./bin/mybin.exe exe.link

软链接的删除知识

  • 因为软链接本质是快捷方式,因此当它链接的内容被删除后,其软链接也相当于被删除了(因为链接无效了,不过其仍然存在文件系统中)

bash 复制代码
rm 连接路径    //rm是通用命令
unlink 连接路径 //只负责删除链接文件
  • **软链接的应用场景:**用于指向可执行文件的路径

硬链接: 不是一个独立文件因为没有独立的inode,硬链接本质是在特定目录的数据块中新增文件名和指向文件的inode映射关系而inode内部都有一个引用计数的计数器来统计有几个文件指向该inode ,只有当所有文件都被删除该inode才会在位图上被抹去。
硬链接会增加权限后面的数字!

硬链接使用:

bash 复制代码
ln 文件路径 连接路径
ln test.txt hard-link

硬链接的删除知识

  • 因为硬链接本质是指向inode的其中一个文件,因此它被删除后不会影响其他同样inode的文件

bash 复制代码
rm 连接路径
unlink 连接路径

**硬链接的应用场景:**用来路径定位,采用硬链接可以进行目录切换

硬链接的补充知识:

  • 目录的硬链接数 = 1(来自父目录) + 1(. 自引用) + 子目录数量(每个子目录贡献一个 ..)因此目录的硬链接树>=2

  • 不能主动给目录添加硬链接,因为可能造成环路问题

  • 但是目录有.和..不也会造成环路吗,这个是搜索不了(因为这两个都是隐藏的)这两个主要是用来路径定位,也正因如此才有相对路径

三.动静态库

1.物理内存和磁盘的交互

内存和磁盘交换数据基本单位为4KB,内存(4KB)叫页框,磁盘(4KB)叫页帧

为什么把交换数据基本单位定为4KB:

  • 减少IO的次数------硬件

  • 基于局部性原理(申请空间的周围可能也会被使用),与加载机制------软件

  • 操作系统如何管理内存

  • 先描述,再组织

通过描述,即对内存的管理就变成对数组的管理,而数组天生自带下标因此也就有了页号,所以我们访问一个内存只需要找到这4KB对应的Page,就能再系统中找到对应的物理页框

所有申请内存的动作,都是在访问内存page数组

2.理解怎么把应用层的内容写到磁盘

  • 在这里补充:struct file存放的只有少数文件属性,主要是为了满足一切皆文件的理念,大部分属性还是存放在inode

  • address_space可以找到struct_page

3.1 理解静态库如何制作与使用

制作

bash 复制代码
lib=libmymath.a		//这个lib是变量名,也是libmymath的前缀名

$(lib): mymath.o	//$(lib)是取出lib变量名的值
	ar -rc $@ $^	//ar是用于创建/修改静态库,
					//-r表示如果有同名就替换,没有就插入;-c表示如果库文件不存在则新建

mymath.o:mymath.c	//.c文件可以自由跳到,预处理、编译、汇编、链接
	gcc -c -o $@ $< //$<表示冒号最右边第一个,如果只有一个依赖文件那么$<==$^

.PHONY:clean
clean:
	rm -f *.o *.a

.PHONY:output
output:
	mkdir -p lib/include	//采用递归创建目录
	mkdir -p lib/mymathlib
	cp *.h lib/include		//把当前工作目录.h的文件拷贝到include
	cp *.a lib/mymathlib

使用

  • (头文件)Linux 系统默认只会在当前目录、系统路径和通过 **-I**参数指定的路径中搜索头文件。若前两个路径都找不到所需头文件,就需要使用 **-I**参数添加自定义搜索路径。

    GCC 查找头文件时,会按顺序搜索:用户指定的 -I 路径 → 系统默认路径 → 当前目录(仅对双引号包含)

  • Linux 系统默认只在当前目录和系统路径中查找库文件。使用 -L 参数可以指定额外的库搜索路径,但还需要配合 -I 参数指定头文件位置。因为代码中只显示了引用的头文件,而实际的库文件名不包含前缀"lib"和后缀".so"或".a"。

3.2 理解动态库制作和使用

制作

bash 复制代码
lib=libmymethod.so

$(lib): mylog.o	//$(lib)其实就是取lib的值
	gcc -shared -o $@ $^	//表示生成共享库格式

mylog.o:mylog.c
	gcc -fPIC -c $< -o $@	//fPIC产生位置无关码

.PHONY: clean
clean:
	rm -f *.o *.so

使用

动态库的使用和静态库一样,不过还需要它会遇到一个问题从而报错!

动态库:编译时只记录"库名"和"路径",运行时由系统加载器去找 .so 文件。如果没放在正确位置或没设置环境变量,就会报错"找不到共享对象"。

处理方法:

第三种详细解释:

就是把自己库所在的路径,添加到系统的环境变量(LD_LIBRARY_PATH)

bash 复制代码
export LD_LIBRARY_PATH= 库所在路径

第四种详细解释:

bash 复制代码
/etc/ld.so.conf.d	//在这里面创建一个文件
/home/yini/practical/lib/mymathlib	//文件存放的是库所在的路径
ldconfig	//设置完刷新一下

补充内容:

ldd命令不能查看静态链接,只能查看动态链接

bash 复制代码
ldd 可执行文件 //查看可执行文件链接的动态库

库的安装其实就是把库和头文件复制到系统路径里面

四.理解动态库

1. 动态库与静态库区别

  1. 动态库是如何加载的

调用动态库函数时,进程会跳转到共享的代码段(在内存中),这段代码是只读的,由操作系统在加载时从磁盘映射到内存,并通过页表让多个进程共享同一物理内存页。

多个进程使用同一个动态库时,各自的全局变量(包括 errno)是相互隔离的,因为每个进程有独立的地址空间和数据段。

五.进程地址空间在理解

1.程序没有加载到内存中(磁盘)

程序在还没有编译的时候有逻辑地址,在加载到内存后又具备了物理地址,一个程序会自带入口地址 ​​​​。
​​

2.程序加载后的地址(进程)

分析:CPU中有一个PC指针告诉CPU该指向那个程序,可执行文件预加载的时候把入口地址传给PC指针,这样CPU就知道去哪里访问,然后通过页表的虚拟地址去寻找物理地址,但是可执行文件内容并没有加载进去,因此触发缺页中断从而把可执行文件内容加载进去,然后此时页表就可以根据虚拟地址查找物理地址

其实逻辑地址现在=虚拟地址,二者只是不同地方对相同东西的不同称呼

3.动态库加载

这个图也阐述了为什么动态库需要位置无关吗(fPIC)因为动态库的函数是用偏移量编的

静态库为什么不谈加载和位置无关呢:

  • 因为静态库是直接拷贝到文件中因此它就没有加载

  • 静态库本质就是文件的代码,因此在文件编的时候顺便也把它编了

关于这部分图片可能大家看里面的内容会觉得很不清晰,主要是这个上传就是感觉有一点点压画质,感觉会糊,这些图片是我以前上课截的图片帮助大家理解!

相关推荐
脏脏a2 小时前
【Linux】进程地址空间
linux·运维·服务器·进程地址空间
后端小张2 小时前
【AI 学习】深入解析卷积神经网络(CNN):理论、实现与应用
人工智能·深度学习·神经网络·opencv·学习·自然语言处理·cnn
努力的小帅2 小时前
Linux_进程间通信(Linux入门到精通)
linux·c++·centos·共享内存·进程通信·命名管道·管道的学习
齐鲁大虾2 小时前
Linux 系统上的开发 C/S 架构的打印程序
linux·c语言·架构
阎*水2 小时前
docker教案
linux·运维
走向IT2 小时前
Python批量修改linux 密码脚本
linux·运维·服务器·python·批量·修改密码
lifewange2 小时前
Linux 系统性能监控核心命令(全覆盖 CPU、内存、磁盘、网络、负载)
linux·网络·php
云计算小黄同学2 小时前
AI × 软件工程 / SRE 学习清单(工程师版)
人工智能·学习·软件工程
一个平凡而乐于分享的小比特2 小时前
Linux系统下的软链接和硬链接的区别和使用
linux·硬链接·软链接