以Linux 0.11为实例,个人总结,不保证正确性。
[文件系统]
磁盘上的数据以块为单位进行读写,每一个块称为一个逻辑块。在理解磁盘的逻辑视图时,以逻辑块为单位来理解。
磁盘上数据按照使用情况分,可以分成以下几个部分:引导块、超级块、i节点位图区、逻辑块节点位图区、i节点区、数据区
引导块中是整个系统的启动代码,只有用于启动的文件系统才有数据,其它的文件系统这个块没有数据(可以这样理解)
超级块中存放的是关于整个文件系统的布局描述的数据
i节点位图区中存放的是i节点区中i节点的使用情况
逻辑块节点位图区中存放的是磁盘中逻辑块的使用情况
i节点区中存放的是对应文件的i节点数据
数据区存放文件数据的部分
[超级块]
超级块中含有描述整个文件系统分布的数据。相应的数据结构如下(fs.h)
struct d_super_block {
unsigned short s_ninodes; //i节点个数
unsigned short s_nzones; //磁盘上全部的逻辑块的个数
unsigned short s_imap_blocks; //i节点位图区所使用的逻辑块的个数
unsigned short s_zmap_blocks; //逻辑块位图区所使用的逻辑块的个数
unsigned short s_firstdatazone; //第一个数据块的逻辑块号
unsigned short s_log_zone_size;
unsigned long s_max_size; // 最大文件长度
unsigned short s_magic; //文件系统魔数
};
struct d_super_block是对磁盘上的超级块的数据描述。
在内存中的超级块的数据结构,除了要存放磁盘中相应的数据外,还要存放一些额外的信息。如读写标志、加锁标志、该文件系统安装的根节点、i节点位图区的高速缓存、设备号等。内存中超级块的数据结构描述如下
struct super_block {
unsigned short s_ninodes;
unsigned short s_nzones;
unsigned short s_imap_blocks;
unsigned short s_zmap_blocks;
unsigned short s_firstdatazone;
unsigned short s_log_zone_size;
unsigned long s_max_size;
unsigned short s_magic;
/* These are only in memory */
struct buffer_head * s_imap[8]; //i节点位图区的高速缓存数组,8个逻辑块的大小
struct buffer_head * s_zmap[8]; //逻辑块位图区的高速缓存数组,8个逻辑块的大小
unsigned short s_dev; //设备号
struct m_inode * s_isup; //被安装文件系统根目录i节点
struct m_inode * s_imount; //该文件系统被安装的i节点
unsigned long s_time; //修改时间
struct task_struct * s_wait; //等待在该超级块上的进程
unsigned char s_lock; //加锁标志
unsigned char s_rd_only; //只读标志
unsigned char s_dirt; //内容是否已修改标志
};
[索引节点]
索引节点(也称i节点)分两种:一个是磁盘上的索引节点,它描述了文件的全部信息;一个是内存中的索引节点信息,它除了包好磁盘中的索引节点的数据外,还包括一些额外的和内存、进程相关的字段。
索引节点描述了一个文件的布局,索引节点以静态形式存于磁盘上,内核把它们读到内存索引节点表以便操作它们。磁盘上的索引节点由如下字段组成:文件所有者标识号、所有者在的用户组标识号、文件类型、文件存取权限、文件存取时间、指向文件的链接数、文件数据的磁盘地址明细表、文件大小。也就是说,文件的索引节点描述了文件的一切信息。
内存中的索引节点额外包括的字段有:内存索引节点的状态、含有该文件的文件系统的逻辑设备号、索引节点号、指向其它内存索引节点的指针、应用数。
索引节点与文件数据有区别的,当对文件进行读写时,改变的是文件数据,但文件的索引节点信息并没有改变。但当改变文件的所有者,改变文件的访问权限时,会使得文件的索引节点信息发生变化,但没有改变文件数据。
[磁盘上的索引节点和内存中的索引节点]
磁盘上的索引节点和内存中的索引节点在0.11中的数据结构分别如下,从代码上可以很明显看到它们之间的差别
/*磁盘上的i节点的数据结构描述*/
struct d_inode {
unsigned short i_mode;
unsigned short i_uid;
unsigned long i_size;
unsigned long i_time;
unsigned char i_gid;
unsigned char i_nlinks;
unsigned short i_zone[9];
};
/*
内存中的i节点的数据结构描述
*/
struct m_inode {
unsigned short i_mode;
unsigned short i_uid;
unsigned long i_size;
unsigned long i_mtime;
unsigned char i_gid;
unsigned char i_nlinks;
unsigned short i_zone[9];
/* these are in memory also */
struct task_struct * i_wait;
unsigned long i_atime;
unsigned long i_ctime;
unsigned short i_dev;
unsigned short i_num;
unsigned short i_count;
unsigned char i_lock;
unsigned char i_dirt;
unsigned char i_pipe;
unsigned char i_mount;
unsigned char i_seek;
unsigned char i_update;
};
i_mode表示文件的访问权限,i_uid表示文件所有者的用户id,i_size表示文件的大小,i_mtime文件的修改时间,i_gid文件所有者的组id,i_nlinks链接数,i_zone逻辑块号数组,i_wait等待在此i节点上的进程,i_atime最后访问时间,i_ctime为i节点创建时间,i_dev为i节点所在的设备号,i_num为i节点号,i_count为i节点被引用次数,i_lock是否加锁标志,i_dirti节点数据是否修改标志,i_pipe管道标志,i_mount安装标志,i_seek搜寻标志,i_update更新标志
[文件与i节点的关系]
文件中的数据是放在磁盘上的数据区的,由一个个的数据块组成。对应文件此文件的i节点中会以某种方式存放保存有文件数据的这些逻辑块号。文件名与i节点对应。这样文件名就通过i节点与实际的文件的数据对应起来。在i节点中存放磁盘逻辑块号的字段是逻辑块号数组,也就是unsigned short i_zone[9]。i_zone[0]~i_zone[6]中直接存放对应的逻辑块号。当文件较大时,会需要使用i_zone[7]存放一次间接逻辑块号;而当文件再大一点,使用一次间接逻辑块号不足以保存时,就是用i_zone[8]存放二次间接逻辑块号。 有一个专门的函数(inode.c/_bmap)来处理文件数据块与磁盘逻辑块之间的映射。
i_zone[0]~i_zone[6]共能存储7个逻辑块号,也就是7K的文件大小。当文件小于7K时,只需要使用直接存储即可。
当文件大于7K时,使用i_zone[7],不过i_zone[7]对应的逻辑块中存放的不是文件数据,而是存储有文件数据的逻辑块号。也就是说,通过i_zone[7]找到逻辑块,然后通过这个逻辑块中的数据去得到真正存储有文件数据的逻辑块号。一个逻辑块大小是1K。也就是1024字节,一个逻辑块号用unsigned short表示,也就是需要2个直接。因此i_zone[7]对应的逻辑块中,存储放有512个逻辑块号,即i_zone[7]表示的文件大小是512K。因此,当文件大小在7k到7k+512K之间时,就需要用到i_zone[7]。
当文件再大一些时,就需要用到i_zone[8]。i_zone[8]中对应的逻辑块中存放有512个逻辑块号,这512个逻辑块中又分别存放有512个逻辑块号。通过二次间接,最后才得到真正存放文件数据的逻辑块号码。因此,i_zone[8]表示的文件大小是512×512K。所以,当文件大小在7K+512K~7K+512K+512*512K时,就需要用到i_zone[8]。当文件再大一点时,linux 0.11中就没法表示了。