1 文件系统
对文件的处理依赖于操作系统中的文件系统;
而操作系统的启动却需要存储在文件系统中的操作系统文件;
——Linux是如何完成这个“鸡生蛋、蛋生鸡”的过程
1.1 Linux系统启动流程
- 加载BIOS的硬件信息与进行自我测试,并依据设置取得第一个可启动的设备;
- 读取并执行第一个启动设备内MBR的boot loader(即grub、spfdisk等程序);
- 依据boot loader的设置加载Kernel,Kernel会检测硬件和加载驱动程序;
- 在硬件驱动成功后,Kernel会主动调用init进程,而init进程会取得run-level信息;
- init执行/etc/rc.d/rc.sysinit文件来准备软件执行的操作环境(如网络、时区);
- init执行run-level的各个服务的启动(使用脚本);
- init执行/etc/rc.d/rc.local文件;
- init执行终端机模拟程序mingetty来启动login进程,最后等待用户登录。
目前与文件系统有关的只有step:1-3。
***Q1:发现了第一个可启动设备,如何读取内核文件?***
由于不同的OS的文件系统格式不一样,如果直接读取肯定是不可能的;所以:要有一个引导装载程序来处理内核文件的加载问题,这个引导装载程序就叫做BootLoader。那么BIOS是如何读取这个BootLoader的?必然是赤裸裸的、对硬件的直接操作。对于磁盘来说,BIOS使用INT 13中断来读取。
***Q2:加载完内核文件,就完成自举了吗?***
加载内核文件以后,内核晚间呗解压到内存中,然后开始测试与驱动各个周边设备,Linux内核会以自己的功能重新检测一次硬件,而不一定会使用BIOS检测到的硬件信息,也就是说:内核此时才开始接管BIOS后的工作。
Linux内核是动态加载内核模块的。内核模块放置在/lib/modules/目录内。
假设此时Linux安装在SATA磁盘内,内核根本不认识,所以需要加载SATA磁盘的驱动程序,但是驱动程序放置在/lib/modules中,现在根本无法读取,怎么办??
- Linux有虚拟文件系统,一般使用文件名/boot/initrd,它也能够通过bootLoader加载到内存中。
- 然后这个文件在内核无法找到根目录的情况下,会被解压并在内存中仿真成一个根目录,此文件系统会提供一个可执行程序,通过该程序就可以加载此文件系统中保存的、启动过程中需要的内核模块。
- 等载入完成后,最终释放虚拟文件系统,并挂载实际的根目录文件系统,会调用/sbin/init来开始后续的正常启动流程。
即:当根目录无法挂载时,initrd会被使用来帮助启动
1.2 文件的存储介质——磁盘
大多数情况下文件是存储到磁盘上,而且大多数情况下,第一个可启动设备也是磁盘。所以需要对文件如何在磁盘上存储有所了解。
***Q3:是不是每个扇区都是一样重要?***
只有第一个扇区特别重要,它记录了整个磁盘的重要信息:
- 主引导分区(Master Boot Record,MBR):可以安装引导加载程序的地方,有446bytes;
- 分区表(partition table):记录整块硬盘分区的状态,有64bytes。
1.2.1 分区
柱面是分区的最小单位,在分区表的64bytes容量中,总共四组记录,每组记录了该区段的起始和结束的柱面号码。
***Q4:能否使用额外的扇区来记录更多的分区信息?***
完全可以,将最后一个分区作为扩展分区,然后再扩展分区的起始处保存逻辑分区的分区记录即可。根据这一行为,有结论:
- /dev/hda[1-n],前四个数字是给主分区和扩展分区使用,逻辑分区的起始号码从5开始;
- 扩展分区只能有一个;
- 能被格式化后作为数据访问的分区是主分区与逻辑分区,扩展分区无法格式化;
- 如果扩展分区被破坏,所有逻辑分区将会被删除。
分区会被选定的文件系统格式化,所以不同的分区可以有不同的文件系统,文件系统的载体就是分区。
1.2.2 MBR
因为第一个扇区所记录的分区表和MBR至关重要,所以如果第一个扇区坏掉了,那么磁盘也就报废了。说回MBR,Linux的启动依赖于MBR中的引导加载程序(BootLoader),那么BootLoader应该有哪些功能?
- 提供菜单:用户可以选择不同的开机项,譬如多重引导;
- 载入内核文件
- 转交其他的loader:将引导加载功能转给其他loader负责。——这意味着,计算机中可以有两个以上的BootLoader。即:出了MBR,BootLoader可以安装在每个分区的引导扇区(boot sector).
***Q5:如何实现多重引导?***
MBR的引导加载程序提供两个菜单,M1可以直接加载windows,M2则将引导加载工作交给第二个分区的启动扇区。总结如下:
- 实际上可开机的内核文件是放置在各分区的;
- loader只会认识自己的系统分区内的可开机内核文件以及其他loader
- loader可以直接指向或间接将管理权移交到另一个loader
***Q6:446bytes可以实现上述功能吗?***
很明显不可能。所以Linux将BootLoader的程序代码分为两个部分:
- step1:执行BootLoader主程序
- step2:主程序加载配置文件:一般来说配置文件都在/boot下。
以grub为例,安装在MBR的grub主程序最重要的任务是:从磁盘中加载内核文件,
***遗留问题:1.BootLoader的原理;***
1.3 文件系统
磁盘分区之后,要进行格式化,在传统的磁盘与文件系统中,一个分区就是只能够被格式化称为一个文件系统,所以我们可以说一个文件系统就是一个分区,但是由于新技术的应用,譬如LVM,一个分区可以格式化为多个文件系统,也可以将多个分区合为一个文件系统。所以,我们在格式化时不再说成是针对分区格式化,而是称呼一个可被挂载的数据为一个文件系统而不是一个分区。
以传统的Ext2对分区进行格式化为例:
-
super block记录此文件系统的整体信息
-
inode:记录文件的属性,一个文件占用一个inode,同时记录此文件数据所在的block号码;
-
block:实际记录文件的内容,若文件太大,会占用多个block;
-
文件系统一开始的inode和block就规划好了,除非重新格式化,否则inode与block固定后就不再变动。如果磁盘很大,那么将所有的inode与block放置在一起管理很不明智。所有分为多个block group进行管理,每个block group都有独立的inode/block/super block系统。
-
data blocks是放置文件内容的地方,在Ext2中,block大小有1KB、2KB和4KB三种;每个block只能存放一个文件;
-
block bitmap——可以知道哪个block是空的,哪个是被使用的;
-
inode bitmap——同上
-
inodes 每个inode大小为128bytes,每个文件只有一个inode,但是如果block很多怎么办?很明显可以使用block作为扩展,但是如何知道哪个block是真实的内容,哪一个是扩展?系统规定:12个直接,一个间接、一个双间接、一个三间接记录区。
Q7:挂载的原理是什么
我们要使用文件系统,需要将文件系统挂载到某一个目录下,那么
mount -t ext2 /dev/sdb1 /mnt/test
发生了什么?
具体应参考Linux内核实现
简略来说,内核会保存挂载目录与挂载文件系统的对应关系,(都会挂载inode编号为2的节点)Linux挂载
1.3.1 文件与目录
- 每个i-node都有一个链接计数,其值为指向该i-node的目录项数;只有当链接计数等于0时,才可删除文件;链接计数保存到stat结构的st_nlink中,是硬链接;
- 符号链接文件的实际内容包含了该符号链接所指向的文件的名字,此为软连接;由此可知:硬链接不能跨文件系统,而软连接可以跨文件系统;
- i节点包含了文件相关的所有信息,stat结构中大多数信息取自i节点,只有两项重要数据存放在目录项中:文件名和i-node编号;
- 重命名操作,只需要删除老的目录项,然后新建一个新的目录项;如果在同一文件系统中移动,那么文件内容不需要移动,这就是mv为什么会很快;
2 静态的文件
当文件被存储在文件系统时,会发生什么?我们又能控制什么?
文件的存储包括两方面:文件内容和文件属性,其中文件内容在block中,文件属性在i-node中。在内核中属性可以用如下数据结构表示:
2.1 文件属性
2.1.1 st_mode:文件类型与mode
- 普通文件
- 目录文件
- 块特殊文件:这种类型的文件提供对设备带缓冲的访问,每次访问以固定长度为单位
- 字符特殊文件:这种类型的文件提供对设备不带缓冲的访问,每次访问长度可变;系统中所有设备,要么是字符特殊文件,要么是块特殊文件
- FIFO
- 套接字
- 符号链接
mode与文件访问权限有关。
2.1.2 用户ID与权限
进程用户ID访问文件的规则
读取文件的对象终归是进程。与一个进程有关的ID有6个或更多:
- 实际用户/组ID:我们究竟是谁?一般从登陆时口令文件获取,在一个登录会话中不会改变,但是超级用户进程有办法改变它们(在进程中再说明)
- 有效用户/组/附属组ID决定了文件访问权限
- 保存的设置用户/组ID的用处在进程中再说明
通常情况下,有效用户/组ID等于实际用户/组ID。但是可以在st_mode中设置一个特殊标志:当执行此文件时,将进程的有效用用户/组ID设置为文件所有者的用户/组ID。——这就是设置用户/组ID位。
所有对以st_mode有:
- 可以设置用户rwx,组rwx,其他rwx。
- 目录的执行位又叫搜索位:当打开一个path时,需要对每一个目录都要有x权限;
- 目录的读权限和执行权限是不同的;
- 为了删除一个现有文件,必须包含对该文件的目录具有写权限和执行权限,对该文件本身不需要读写权限;
进程每次打开、创建、和删除一个文件,内核都会进行权限测试:
- 若进程的有效用户ID为0,则允许访问;
- (进程的有效用户ID等于文件所有者 AND 相应访问位被设置 ) --> 允许访问;
- (进程的有效组ID或附属组ID等于文件的组ID AND 相应组访问位被设置 ) --> 允许访问;
- 若其他用户适当的访问权限位被设置,则允许访问;否则拒绝访问。
设置文件ID的规则
- 新文件的用户ID设置为进程的有效用户;
- 组ID的设置可以:1)新文件的组ID可以是进程的有效组ID;2)新文件的组ID可以使它所在目录的组ID————这取决于新文件的组ID取决于它所在的目录的设置组ID位是否被设置;
- 更改文件用户ID:只有超级用户进程能更改该文件的用户ID;
- 更改文件组ID:如果进程拥有此文件(进程的有效用户ID等于该文件的用户ID),非超级用户进程可以改变该文件的组ID为:进程的有效组ID或进程的附属组ID
为什么超级用户才能更改文件的所有者?这样做的原因是防止用户改变其文件的所有者从而摆脱磁盘空间限额对他们的限制
文件mod
- 创建文件时如何指定mod?使用文件模式创建屏蔽字,当创建一个新文件或新目录时,在文件模式创建屏蔽字中为1的位,在文件mode中的相应位一定会被关闭;
- 当进程的有效用户ID等于文件的所有者ID或进程必须具有超级用户权限,就可以改变一个文件的权限位;
Q1:修改mod的注意点
- 如果新创建文件的组ID不等于调用进程所属的组ID或者附属组ID,而且进程没有超级用户权限,那么设置组ID位会被自动清除,————这防止了用户创建一个设置组ID文件,而该文件是由并非该用户所属组拥有的,
- 如果没有超级用户权限,那么试图在普通文件上设置粘着位,则会被自动清除。
如果一个目录设置了粘着位,只有对该目录具有写权限的用户并且满足下列条件之一,才能删除或重命名该目录下的文件:
- 拥有此文件;
- 拥有此目录
- 是超级用户
回想一下,之前删除文件的条件是:只需要可以读写目录即可;/tmp是粘着位的最佳使用者,任何用户都可以使用这个目录,但是不能删除其他人的文件。
2.1.3 文件长度
- 文件长度只对普通文件、目录文件和符号链接有意义。
- 对于目录,文件长度通常是一个数(如16或512)的整数倍;
- 对于符号链接,文件长度是在文件名中的实际字节数
- 文件空洞是字节0
- 可以将文件截断为length,若文件原来长度大于length,则length之外的数据不能再访问;若原来长度小于length,则文件长度增加,创建了一个空洞
2.1.4 remove与rename
***Q2:文件什么时候可以被删除?***
1)链接计数达到0;2)没有进程打开此文件。
- 当关闭一个文件时,内核首先检查打开该文件的进程数
- 如果为0,则检查链接计数,如果为0,则删除该文件的内容;
这种特性常用于确保程序崩溃时,所创建的临时文件也不会遗留下来:
创建一个文件之后,立刻unlink;
2.1.5 符号链接
符号链接处理的时候经常需要考虑语义:处理的究竟是符号链接文件本身还是符号链接所指向的文件?
2.1.6 文件的时间
stat的三个字段:st_atim 、st_mtim代表的是文件数据的最后访问时间、最后修改时间;st_ctim代表i-node的最后更改时间。而一个文件的访问和修改时间可以使用函数来更改。
2.1.7 设备特殊文件
st_dev和st_rdev
- 每个文件系统所在的存储设备都由其主、次设备号表示,设备号所有的数据类型dev_t。主设备号标识设备驱动程序,次设备号标识特定的子设备;
- 系统中与每个文件名关联的st_dev值是文件系统的设备号,该文件系统包含了这一文件名以及其对应的i-node;
- 只有字符特殊文件和块特殊文件才有st_rdev,包含实际设备的设备号