- ext2文件系统:
- ext2文件系统中除了开始的1KB大小的
Boot Block
外,其余都是一个个的块组(Block Group
)。下面是一个块组的结构:
super block | GDT | block bitmap | inode bitmap | inode table | data blocks(一个block组的主要部分) |
---|
inode
:其本质为结构体,存储文件的属性信息。如:权限、类型、大小、时间、用户、盘块位置……也叫作文件属性管理结构,大多数的inode都存储在磁盘上,少量常用、近期使用的inode会被缓存到内存中。每个文件的inode仅有一份。一个块组中的所有inode组成了inode table。mke2fs
格式化工具默认策略是在块组中每8KB分配一个inode。由于一个块组中的绝大部分是数据块,因此也近似认为是对于数据块而言,每8KB分配一个inode。dentry
:目录项,其本质依然是结构体,重要成员变量有两个 {文件名,inode, …},而文件内容(data)保存在磁盘盘块中。注意:创建一个文件的硬链接其实就是增加一个该文件的dentry,不会改变该文件的inode个数,更不会影响该文件的data block。- 对于
unlink
函数,它删除所打开的临时文件的dentry,使之具备被释放的条件,并不等于删除了该文件。只有当使用该文件的所有进程都结束后,该文件才会被真正释放。
- 文件操作:
stat
函数族:stat\fstat\lstat
。一般使用fstat
较多,因为它的参数1是一个文件描述符,较方便。它们的参数2struct stat *buf
是一个传出参数。注意这个结构体,其中包含的inode信息很重要。比如:计算一个文件的大小可借助fstat
函数得到该传出参数中的st_size
分量,即为文件大小。- 使用系统调用
stat
查看文件类型时有一个问题,若它的参数是一个符号链接(软链接),那么stat会穿透该符号链接追踪到实际真正的文件并给出该实际文件的类型而非符号链接的类型。而lstat
函数就不会出现穿透符号链接的问题,它会给出符号链接的文件类型。 struct stat
结构体是系统维护的一个描述文件信息的结构体,其中st_mode
保存文件的类型、权限等信息,注意,st_mode也是一个位图(bitmap
),共有16位,前四位描述文件类型,接下来三位描述超级权限,再接下来的9位每三位一组描述用户、组、其他人的读\写\执行权限。通常,使用系统调用stat
查看文件大小、文件类型以及文件权限等。详细信息参见man page中关于stat的介绍。- 上述2中的三位超级权限位从左到右依次是:
setGID(设置组ID位)
、setID(设置用户ID位)
、黏住位。黏住位是针对以前计算机内存较小时的情况设计的,现已摒弃;关于setID
位,需要进一步区分进程的两个ID:有效ID(EID)和实际ID(UID)。
- 使用系统调用
truncate
函数族:ftruncate\truncate
:修改一个文件的大小。常用的是ftruncate
函数,因接收一个文件描述符。该族函数拓展一个文件时不需要跟一次写操作,这是和lseek
函数不同的地方。link\unlink
函数族:link
函数:创建一个硬链接。当rm
删除文件时,只是删除了目录下的记录项和把inode
硬链接计数减1,当硬链接计数减为0时,才会真正的删除文件。另外,通常不允许创建目录的硬链接;创建目录项以及增加硬链接计数应当是一个原子操作。unlink
函数的作用实际上是减少一个文件的dentry到inode的链接数。因此,我们删除文件,从某种意义上说,只是让文件具备了被释放的条件。- unlink函数的特征:清除文件时,如果文件的硬链接数到0了,没有dentry对应,该文件仍不会马上被释放。要等到所有打开该文件的进程关闭该文件后,系统才会挑时间将该文件释放掉。
- 注意:若在
unlink
之后继续对该文件执行write
操作,程序并不会出错,因为系统调用是通过文件结构体来访问文件,不通过dentry
路径来访问;况且,对文件的写操作也并不是直接写到文件中,而是写到操作系统的内核缓冲区中。
- 隐式回收:当进程结束运行时,所有该进程打开的文件会被关闭,申请的内存空间会被释放。所以,例如,在一个程序中使用
malloc
申请的动态内存空间即使在后面忘记free
掉也没关系,程序结束后会自动释放掉,但若该程序一直不结束那么就有内存泄露的风险,比如在服务器程序中要格外注意这一点。
- 目录操作:
- 文件和目录的权限比较:
类别\权限 | r | w | x |
---|---|---|---|
文件 | 文件的内容可以被查看:cat、more、less… | 内容可以被修改:vi、> … | 可以运行产生一个进程:./文件名 |
目录 | 目录可以被浏览:ls、tree… | 创建、删除、修改文件:mv、touch、mkdir… | 可以被打开、进入:cd |
注意:
目录文件也是“文件”。其文件内容是该目录下所有子文件的目录项(dentry
)。 可以尝试用vim打开一个目录,里面的内容就是一个个的dentry
。所以,目录和普通文件其实没有区别。
opendir
、readdir
、clodedir
目录操作函数。- 递归遍历目录
- 重定向:
dup
和dup2
函数,需从文件描述符的原理上理解,尤其是dup2
函数。它们都可用来复制一个现存的文件描述符,使两个文件描述符指向同一个file
结构体。- 注意区分两种情况:
1)如果两个文件描述符指向同一个file结构体,File Status Flag
和读写位置只保存一份在file
结构体中,并且file
结构体的引用计数是2。
2)如果两次open
同一文件得到两个文件描述符,则每个描述符各自对应一个不同的file
结构体,它们可以有不同的File Status Flag
和读写位置。 - dup2函数:
int dup2(int oldfd, int newfd);
将文件描述符oldfd
复制给文件描述符newfd
,也即让newfd
指向oldfd
所指向的文件,例如在命令行执行的重定向命令可由该函数模拟。
- 虚拟文件系统(VFS):把不同的文件系统统一成相同的格式,屏蔽不同文件系统之间的差别。注意,windows系统中没有VFS,所以假如一台电脑中同时装有windows和linux双系统,那么在windows下看不到linux中的文件,而在linux下可以看到windows下的文件甚至可以操作它们,就是因为linux下的VFS。如下的VFS过程理解即可,实际开发中应用不多。
VFS虚拟文件系统:
以不同的进程打开文件为例:
- Linux系统有7种文件:
文件(-
)
目录(d
)
符号链接(l
,即软链接)
套接字(s
)
块设备(b
)
字符设备(c
)
管道(p
)
其中,前三种文件占用磁盘空间,后四种文件不占用磁盘空间(并不是真正的文件,也称伪文件)。