http://blog.sina.com.cn/s/blog_6b94d5680101vfqv.html

Linux内核源代码情景分析---第五章 文件系统

 5.1 概述

构成一个操作系统最重要的就是 进程管理 与 文件系统;
有些操作系统有进程管理而没有文件系统,有些操作系统有文件系统而没有进程管理(MSDOS);两者都没有那就不是操作系统了;
狭义的文件:指磁盘文件,进入指可以是有序地存储在任何介质中(包括内存)的一组信息。
广义的文件:(unix把外部设备也当成文件)凡是可以产生或消耗信息的都是文件;(网络环境中用来接收报文的“插口机制”:它不代表存储着的信息,但是插口的发送端消耗信息,接收端则产生信息)
 
文件系统的含义,模糊如下:
1 : 指一种特定的文件格式(EXT2 FAT16 NTFS FAT32等)
2:指按特定格式进行了格式化的一块存储介质(当我们说安装或卸载一个文件系统,就是这个意思)
3:指操作系统中(通常为内核中)用来管理文件系统以及对文件进行操作的机制及其实现
 
除linux本身的文件系统ext2外,linux还需要支持各种不同的文件系统,为了达到这个目的,linux引入了VFS虚拟文件系统;这个抽象的界面主要由一组标准的、抽象的  统一的 文件操作构成,以系统调用的形式提供于用户程序,如read() write()  lseek()等等,然后不同的文件系统再各自实现自己的操作。我们可以把内核比拟为PC机的主板,VFS比拟为主板上的插槽,每个具体的文件系统就好像一块接口卡,不同的接口卡上有不同的电子线路; 不同的文件系统通过不同的程序来实现其各种功能,但是与VFS之间的界面则是有明确定义的,这个界面的主题就是一个file_operations数据结构。
struct file {
struct list_head f_list;
struct dentry *f_dentry;
struct vfsmount         *f_vfsmnt;
struct file_operations *f_op;
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;
unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
int f_error;
unsigned long f_version;
void *private_data;
}
每种文件系统都有自己的file_operations数据结构,结构中成分几乎全是函数指针,所以实际上是函数跳转表;例如read() 就是指向具体文件系统用来实现读文件操作的入口函数。如果具体的文件系统不支持某种操作,其file_operations结构中的相应函数指针就是NULL。
 
每个进程通过“打开文件”open()与具体的文件建立起连接,或者说建立起一个读写的“上下文”。这个连接以一个file数据结构作为代表,结构中有个file_operations结构指针f_op, 将file数据结构中的file_operations结构指针f_op设置成指向某个具体的file_operations结构,就指定了这个文件所属的文件系统,并且与具体文件系统所提供的一组函数挂上钩;
 

进程与文件的连接,即“已打开文件”,是进程的一项“财产”,归具体的进程所有。进程的task_struct结构中有两个指针:fs   files, 一个指向fs_struct数据结构,是关于文件系统的信息; 一个指向files_struct数据结构,是关于已打开文件的信息;

struct fs_struct {
    atomic_t count;
    rwlock_t lock;
    int umask;
    struct dentry * root, * pwd, * altroot;
    struct vfsmount * rootmnt, * pwdmnt, * altrootmnt;
};
结构中有六个指针。前三个是dentry 结构指针,就是 root  pwd altroot; 这些指针各自指向代表着一个"目录项"的dentry 数据结构,里面记录着文件的各项属性,如文件名、访问权限等等。其中pwd 则指向进程当前所在的目录;而root所指向的dentry 结构代表着本进程的"根目录",那就是当用户登录进入系统时所"看到"的根目录;至于altroot则为用户设置的"替换根目录",我们以后还会讲到。实际运行时这三个目录不一定都在同一个文件系统中,例如进程的根目录通常是安装于“\”节点上的ext2文件系统中,而当前工作目录则可能安装于/dos的一个DOS文件系统中。在文件系统的操作中这些安装点起着重要的作用而常常要用到,所以后三个指针就各自指向代表着这些"安装" 的vfsmount数据结构。注意,fs_struct 结构中的信息都是与文件系统和进程有关的,带有全局性的(对具体的进程而言),而与具体的已打开文件没有什么关系。例如进程的根目录在ext2文件系统中,当前工作目录在DOS文件系统中,而一个具体的已打开文件却可能是设备文件。
struct files_struct {
    atomic_t count;
    rwlock_t file_lock;
    int max_fds;
    int max_fdset;
    int next_fd;
    struct file ** fd;
    fd_set *close_on_exec;
    fd_set *open_fds;
    fd_set close_on_exec_init;
    fd_set open_fds_init;
    struct file * fd_array[NR_OPEN_DEFAULT];
};
struct file {
struct list_head f_list;
struct dentry *f_dentry;
struct vfsmount         *f_vfsmnt;
struct file_operations *f_op;
atomic_t f_count;
unsigned int  f_flags;
mode_t f_mode;
loff_t f_pos;
unsigned long  f_reada, f_ramax, f_raend, f_ralen, f_rawin;
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
int f_error;
unsigned long f_version;
void *private_data;
}
files_struct 结构中的主体就是一个file结构数组struct file * fd_array[NR_OPEN_DEFAULT];每打开一个文件以后,进程就通过一个“打开文件号”fid来访问这个文件,而fid就是数组 fd_array[NR_OPEN_DEFAULT]的下标。每个file结构中有个指针f_op,指向该文件所属文件系统的file_operations数据结构。同时,file结构中还有个指针f_dentry,指向该文件的dentry数据结构。那么,为什么不干脆把文件的dentry结构放在file结构里面,而只是让file结构通过指针来指向它呢?这是因为一个文件只有一个dentry数据结构,而可能有多个进程打开它,甚至同一个进程也可能多次打开它而建立起多个读写上下文。同理,每种文件系统只有一个file_operations数据结构,它既不专属于某个特定的文件,更不专属于某个特定的上下文。
      每个文件除有一个"目录项"即dentry数据结构以外,还有一个"索引节点"即inode数据结构, 里面记录着文件在存储介质上的位置与分布等信息。同时,dentry结构中有个inode结构指针d_inode指向相应的inode结构。读者也许要问,既然一个文件的dentry结构和inode结构都在从不同的角度描述这个文件各方面的属性,那为什么不"合二为一",而要"一分为二"呢?其实,dentry结构与inode结构所描述的目标是不同的,因为一个文件可能有好几个文件名,而通过不同的文件名访问同一个文件时的权限也可能不同。所以,dentry结构所代表的是逻辑意义上的文件,记录的是其逻辑上的属性。而inode结构所代表的是物理意义上的文件,记录的是其物理上的属性;它们之间的关系是多对一的关系。
     前面我们说虚拟文件系统VFS与具体的文件系统之间的界面的"主体"是file_operations数据结构,是因为除此之外还有一些其它的数据结构。其中主要的还有与目录项相联系的dentry_operations 数据结构和与索引节点相联系的inode_operations数据结构。这两个数据结构中的内容也都是一些函数指针,但是这些函数大多只是在打开文件的过程中使用,或者仅在文件操作的"底层"使用〔如分配空间〕,所以不像file_operations结构中那些函数那么常用,或者不那么有"知名度"。
struct dentry_operations {
int (*d_revalidate)(struct dentry *, int);
int (*d_hash) (struct dentry *, struct qstr *);
int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
int (*d_delete)(struct dentry *);
void (*d_release)(struct dentry *);
void (*d_iput)(struct dentry *, struct inode *);
};

这里d_delete是指向具体文件系统的"删除文件"操作的入口函数,d_release则用于"关闭文件" 操作。还有个函数指针d_compare很有意思,它用于文件名的比对。读者可能感到奇怪,文件名的比对就是字符串的比对,这难道也是因文件系统而异? 其实不足为奇,想一想:有的文件系统中文件名的长度限于8个字符,有的则可以有255个字符;有的文件系统容许在文件名里有空格,有的则不容许;有的支持汉字,有的则不支持。所以,文件名的比对确实因文件系统而异。

总之,具体文件系统与虚拟文件系统VFS间的界面是一组数据结构,包括file_operations  dentry_operations inode_operations还有其它(此处暂从略). 原则上每种文件系统都必须在内核中提供这些数据结构,后面我们还要深入讨论。

 

在file结构体中维护File Status Flag(file结构体的成员f_flags)和当前读写位置(file结构体的成员f_pos)。
在上图中,进程1和进程2都打开同一文件,但是对应不同的file结构体【通过该进程的task_struct->files_struct中的数组fd_array[xx] 与 fd(即数组下标)来找到本进程打开的该文件对应的file结构体】,因此可以有不同的File Status Flag和读写位置。
file结构体中比较重要的成员还有f_count,表示引用计数(Reference Count),后面我们会讲到,dup、fork等系统调用会导致多个文件描述符指向同一个file结构体,例如有fd1和fd2都引用同一个file结构体,那么它的引用计数就是2,当close(fd1)时并不会释放file结构体,而只是把引用计数减到1,如果再close(fd2),引用计数就会减到0同时释放file结构体,这才真的关闭了文件
每个file结构体都指向一个file_operations结构体,这个结构体的成员都是函数指针,指向实现各种文件操作的内核函数。比如在用户程序中read一个文件描述符,read通过系统调用进入内核,然后找到这个文件描述符所指向的file结构体,找到file结构体所指向的file_operations结构体,调用它的read成员所指向的内核函数以完成用户请求。在用户程序中调用lseek、read、write、ioctl、open等函数,最终都由内核调用file_operations的各成员所指向的内核函数完成用户请求。file_operations结构体中的release成员用于完成用户程序的close请求,之所以叫release而不叫close是因为它不一定真的关闭文件,而是减少引用计数,只有引用计数减到0才关闭文件。对于同一个文件系统上打开的常规文件来说,read、write等文件操作的步骤和方法应该是一样的,调用的函数应该是相同的,所以图中的三个打开文件的file结构体指向同一个file_operations结构体如果打开一个字符设备文件,那么它的read、write操作肯定和常规文件不一样,不是读写磁盘的数据块而是读写硬件设备,所以file结构体应该指向不同的file_operations结构体,其中的各种文件操作函数由该设备的驱动程序实现。每个file结构体都有一个指向dentry结构体的指针,“dentry”是directory entry(目录项)的缩写。我们传给open、stat等函数的参数的是一个路径,例如/home/akaedu/a,需要根据路径找到文件的inode。为了减少读盘次数,内核缓存了目录的树状结构,称为dentry cache,其中每个节点是一个dentry结构体,只要沿着路径各部分的dentry搜索即可,从根目录/找到home目录,然后找到akaedu目录,然后找到文件a。dentry cache只保存最近访问过的目录项,如果要找的目录项在cache中没有,就要从磁盘读到内存中。
每个dentry结构体都有一个指针指向inode结构体。inode结构体保存着从磁盘inode读上来的信息。在上图的例子中,有两个dentry,分别表示/home/akaedu/a和/home/akaedu/b,它们都指向同一个inode,说明这两个文件互为硬链接。inode结构体中保存着从磁盘分区的inode读上来信息,例如所有者、文件大小、文件类型和权限位等。每个inode结构体都有一个指向inode_operations结构体的指针,后者也是一组函数指针指向一些完成文件目录操作的内核函数。和file_operations不同,inode_operations所指向的不是针对某一个文件进行操作的函数,而是影响文件和目录布局的函数,例如添加删除文件和目录、跟踪符号链接等等,属于同一文件系统的各inode结构体可以指向同一个inode_operations结构体。
    inode结构体有一个指向super_block结构体的指针。super_block结构体保存着从磁盘分区的超级块读上来的信息,例如文件系统类型、块大小等。super_block结构体的s_root成员是一个指向dentry的指针,表示这个文件系统的根目录被mount到哪里,在上图的例子中这个分区被mount到/home目录下。
    file、dentry、inode、super_block这几个结构体组成了VFS的核心概念。对于ext2文件系统来说,在磁盘存储布局上也有inode和超级块的概念,所以很容易和VFS中的概念建立对应关系。而另外一些文件系统格式来自非UNIX系统(例如Windows的FAT32、NTFS),可能没有inode或超级块这样的概念,但为了能mount到Linux系统,也只好在驱动程序中硬凑一下,在Linux下看FAT32和NTFS分区会发现权限位是错的,所有文件都是rwxrwxrwx,因为它们本来就没有inode和权限位的概念,这是硬凑出来的。
补充:
     在这个图上没有反应file_operations与inode的关系,特去查看了源码(/usr/include/fs.h)知
     file_operations结构体中每个函数指针所指向的操作函数都是需要传进一个inode表项结构体的.

Inode分为内存中的inode和文件系统中的inode,我们这里说的是文件系统中的inode。
文件系统结构
    结合上面命令的输出结果,一个分区一般含有多个block group,比如上面看到的40个。而每个block group都有superblock、group description、block bitmap、inode bitmap、inode table、data blocks,比如上面的Backup Superblock占用1个block(4KB)、Group descriptors占用1个block(4KB)、Block bitmap占用1个block(1KB)、Inode bitmap占用1个block(1KB)、Inode table占用1001个block(512.5KB)。
    Superblock记录整个partition的block和inode的总量,已使用和未使用的inode和block的数量,1个block和1个inode的大小,filesystem的挂载时间/最后写入时间/最后检查时间、标示该文件系统是否被挂载的valid bit(0标示未挂载,1标示已挂载)。是MBR中的Superblock的backup。
    Group descriptors描述由何处开始记录数据,是MBR中的Group descriptors的backup。
    Block bitmap记录哪个block是空闲的。
    Inode bitmap记录哪个inode是空闲的。
    Inode table存放inode数据。
    Data blocks存放block数据。

当一个分区被格式化为ext2或ext3的文件系统的时候,会自动产生inode number。
    inode number可以决定在这个分区中存储多少文件或目录,因为每个文件和目录都会有与之相对应的inode number。
     每个inode number都有对应的inode table。
inode table记录这个inode number对应文件所对应的metadata(元数据)。
metadata的主要作用是描述资料的属性:
再来了解一下文件系统如何存取文件的
1、根据文件名,通过Directory里的对应关系,找到文件对应的Inode number
2、再根据Inode number读取到文件的Inode table
3、再根据Inode table中的Pointer读取到相应的Blocks
Directory,他不是我们通常说的目录,而是一个列表,记录了一个文件/目录名称对应的Inode number

那么文件系统是如何运行的呢?这与操作系统的文件数据有关。较新的操作系统的文件数据除了文件实际内容外, 通常含有非常多的属性,例如 Linux 操作系统的文件权限(rwx)与文件属性(拥有者、群组、时间参数等)。 文件系统通常会将这两部份的数据分别存放在不同的区块,权限与属性放置到 inode 中,至于实际数据则放置到 data block 区块中。 另外,还有一个超级区块 (superblock) 会记录整个文件系统的整体信息,包括 inode 与 block 的总量、使用量、剩余量等。
每个 inode 与 block 都有编号,至于这三个数据的意义可以简略说明如下:
superblock:记录此 filesystem 的整体信息,包括inode/block的总量、使用量、剩余量, 以及文件系统的格式与相关信息等;
inode:记录文件的属性,一个文件占用一个inode,同时记录此文件的数据所在的 block 号码;
block:实际记录文件的内容,若文件太大时,会占用多个 block 。
由于每个 inode 与 block 都有编号,而每个文件都会占用一个 inode ,inode 内则有文件数据放置的 block 号码。 因此,我们可以知道的是,如果能够找到文件的 inode 的话,那么自然就会知道这个文件所放置数据的 block 号码, 当然也就能够读出该文件的实际数据了。这是个比较有效率的作法,因为如此一来我们的磁盘就能够在短时间内读取出全部的数据, 读写的效能比较好啰。
我们将 inode 与 block 区块用图解来说明一下,如下图所示,文件系统先格式化出 inode 与 block 的区块,假设某一个文件的属性与权限数据是放置到 inode 4 号(下图较小方格内),而这个 inode 记录了文件数据的实际放置点为 2, 7, 13, 15 这四个 block 号码,此时我们的操作系统就能够据此来排列磁盘的阅读顺序,可以一口气将四个 block 内容读出来! 那么数据的读取就如同下图中的箭头所指定的模样了。

Linux 的 EXT2 文件系统(inode)

我们介绍过 Linux 的文件除了原有的数据内容外,还含有非常多的权限与属性,这些权限与属性是为了保护每个用户所拥有数据的隐密性。 而前一小节我们知道 filesystem 里面可能含有的 inode/block/superblock 等。为什么要谈这个呢?因为标准的 Linux 文件系统 Ext2 就是使用这种 inode 为基础的文件系统啦!

而如同前一小节所说的,inode 的内容在记录文件的权限与相关属性,至于 block 区块则是在记录文件的实际内容。 而且文件系统一开始就将 inode 与 block 规划好了,除非重新格式化(或者利用 resize2fs 等命令变更文件系统大小),否则 inode 与 block 固定后就不再变动。但是如果仔细考虑一下,如果我的文件系统高达数百GB时, 那么将所有的 inode 与 block 通通放置在一起将是很不智的决定,因为 inode 与 block 的数量太庞大,不容易管理。

为此之故,因此 Ext2 文件系统在格式化的时候基本上是区分为多个区块群组 (block group) 的,每个区块群组都有独立的 inode/block/superblock 系统。感觉上就好像我们在当兵时,一个营里面有分成数个连,每个连有自己的联络系统, 但最终都向营部回报连上最正确的信息一般!这样分成一群群的比较好管理啦!整个来说,Ext2 格式化后有点像底下这样:


图1.3.1、ext2文件系统示意图(注1)

在整体的规划当中,文件系统最前面有一个启动扇区(boot sector),这个启动扇区可以安装启动管理程序, 这是个非常重要的设计,因为如此一来我们就能够将不同的启动管理程序安装到个别的文件系统最前端,而不用覆盖整颗硬盘唯一的 MBR, 这样也才能够制作出多重引导的环境啊!至于每一个区块群组(block group)的六个主要内容说明如后:

data block (数据区块)

data block 是用来放置文件内容数据地方,在 Ext2 文件系统中所支持的 block 大小有 1K, 2K 及 4K 三种而已。在格式化时 block 的大小就固定了,且每个 block 都有编号,以方便 inode 的记录啦。 不过要注意的是,由于 block 大小的差异,会导致该文件系统能够支持的最大磁盘容量与最大单一文件容量并不相同。 因为 block 大小而产生的 Ext2 文件系统限制如下:(注2)

磁盘上的inode 与 内存中(VFS)的 inode 的区别

在容易引起混淆的地方我将把把内存中的inode结构称为VFS inode,而文件系统以EXT2为代表,把Ext2 inode作为磁盘上的inode代表。首先需要分别对内存中的inode与磁盘上的inode做一下简单的描述:

<内存中的inode结构:>VFS inode包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。它是linux管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁。inode结构中的静态信息取自物理设备上的文件系统,由文件系统指定的函数填写,它只存在于内存中,可以通过inode缓存访问。虽然每个文件都有相应的inode结点,但是只有在需要的时候系统才会在内存中为其建立相应的inode数据结构,建立的inode结构将形成一个链表,我们可以通过遍历这个链表去得到我们需要的文件结点,VFS也为已分配的inode构造缓存和hash table,以提高系统性能。inode结构中的struct inode_operations *iop为我们提供了一个inode操作列表,通过这个列表提供的函数我们可以对VFS inode结点进行各种操作。每个inode结构都

有一个i结点号i_ino,在同一个文件系统中每个i结点号是唯一的。

<磁盘上的inode:>

EXT2通过使用inode来定义文件系统的结构以及描述系统中每个文件的管理信息,每个文件都有一个inode且只有一个,即使文件中没有数据,其索引结点也是存在的。每个文件用一个单独的Ext2 inode结构来描述,而且每一个inode都有唯一的标志号。Ext2 inode为内存中的inode结构提供了文件的基本信息,随着内存中inode结构的变化,系统也将更新Ext2 inode中

相应的内容。Ext2 inode对应的是Ext2_inode结构。

从上面的描述,我们可以对内存中inode与磁盘中inode做出比较:

位置:VFS inode结构位于内存中,而Ext2_inode位于磁盘。

生存期:VFS inode在需要时才会被建立,如果系统断电,此结构也随之消失。

而Ext2_inode的存在与系统是否上电无关,而且无论文件是否包含

数据,Ext2_inode都是存在的。

唯一性:两者在自己的作用域中都是唯一的。

关系:VFS inode是Ext2 inode的抽象、映射与扩充,而后者是前者的静态

信息部分,也是对前者的具体化、实例化和持久化。

操作:对VFS inode的操作具有通用性,对文件系统inode的操作则是文件系

统相关的,依赖于特定的实现。

组织管理:系统通过VFS inode链表来对其进行组织,并且为了提高访问效率

相应地构造了inode构造缓存和hash table。

Ext2 inode的信息位于EXT2文件系统的划分的块组中,在每个块组

中包含相应的inode位图、inode表指定具体的inode信息,每个

inode对应Ext2_inode结构。

Linux 的 EXT2 文件系统(inode)

*索引节点对象由inode结构体表示,定义文件在linux/fs.h中
*/
struct inode {
        struct hlist_node       i_hash;              
        struct list_head        i_list;              
        struct list_head        i_dentry;            
        unsigned long           i_ino;              
        atomic_t                i_count;            
        umode_t                 i_mode;              
        unsigned int            i_nlink;            
        uid_t                   i_uid;              
        gid_t                   i_gid;              
        kdev_t                  i_rdev;              
        loff_t                  i_size;              
        struct timespec         i_atime;            
        struct timespec         i_mtime;            
        struct timespec         i_ctime;            
        unsigned int            i_blkbits;          
        unsigned long           i_blksize;          
        unsigned long           i_version;          
        unsigned long           i_blocks;            
        unsigned short          i_bytes;            
        spinlock_t              i_lock;              
        struct rw_semaphore     i_alloc_sem;        
        struct inode_operations *i_op;              
        struct file_operations  *i_fop;              
        struct super_block      *i_sb;              
        struct file_lock        *i_flock;            
        struct address_space    *i_mapping;          
        struct address_space    i_data;              
        struct dquot            *i_dquot[MAXQUOTAS];
        struct list_head        i_devices;          
        struct pipe_inode_info  *i_pipe;            
        struct block_device     *i_bdev;            
        unsigned long           i_dnotify_mask;      
        struct dnotify_struct   *i_dnotify;          
        unsigned long           i_state;            
        unsigned long           dirtied_when;        
        unsigned int            i_flags;            
        unsigned char           i_sock;              
        atomic_t                i_writecount;        
        void                    *i_security;        
        __u32                   i_generation;        
        union {
                void            *generic_ip;        
        } u;
};
 
数据结构inode中有一个union成员 u,如下,不同的文件系统,将这个u解析成不同的数据结构。
struct inode {
struct list_head i_hash;
struct list_head i_list;
struct list_head i_dentry;
struct list_head i_dirty_buffers;
unsigned long i_ino;
atomic_t i_count;
kdev_t i_dev;
umode_t i_mode;
nlink_t i_nlink;
uid_t i_uid;
gid_t i_gid;
kdev_t i_rdev;
loff_t i_size;
time_t i_atime;
time_t i_mtime;
time_t i_ctime;
unsigned long i_blksize;
unsigned long i_blocks;
unsigned long i_version;
struct semaphore i_sem;
struct semaphore i_zombie;
struct inode_operations *i_op;
struct file_operations *i_fop;
struct super_block *i_sb;
wait_queue_head_t i_wait;
struct file_lock *i_flock;
struct address_space *i_mapping;
struct address_space i_data;
struct dquot *i_dquot[MAXQUOTAS];
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
unsigned long i_dnotify_mask;
struct dnotify_struct *i_dnotify;
unsigned long i_state;
unsigned int i_flags;
unsigned char i_sock;
atomic_t i_writecount;
unsigned int i_attr_flags;
__u32 i_generation;
union {
struct minix_inode_info minix_i;
struct ext2_inode_info ext2_i;
struct hpfs_inode_info hpfs_i;
struct ntfs_inode_info ntfs_i;
struct msdos_inode_info msdos_i;
struct umsdos_inode_info umsdos_i;
struct iso_inode_info isofs_i;
struct nfs_inode_info nfs_i;
struct sysv_inode_info sysv_i;
struct affs_inode_info affs_i;
struct ufs_inode_info ufs_i;
struct efs_inode_info efs_i;
struct romfs_inode_info romfs_i;
struct shmem_inode_info shmem_i;
struct coda_inode_info coda_i;
struct smb_inode_info smbfs_i;
struct hfs_inode_info hfs_i;
struct adfs_inode_info adfs_i;
struct qnx4_inode_info qnx4_i;
struct bfs_inode_info bfs_i;
struct udf_inode_info udf_i;
struct ncp_inode_info ncpfs_i;
struct proc_inode_info proc_i;
struct socket socket_i;
struct usbdev_inode_info        usbdev_i;
void *generic_ip;
} u;
}

所以,inode结构中的这个union反映了各种文件系统在部分数据结构上的不同,而file_operations

结构则反应了它们在算法(操作)上的不同。当一个文件系统代表着磁盘(或其它介质)上按特定格式组织的文件时,对每个文件的操作最终都要转化为对某一部分磁盘介质的操作,所以从层次的观点来看,在"文件系统"以下还有一层"设备驱动"。可是,既然设备〔实际上是设备驱动)也是视作文件的,那么作为与文件"平级"的磁盘设备"文件"与作为文件系统"底层"的磁盘"设备"又有什么区别呢?这实际上反映了对磁盘介质上的数据的两种不同观点,一种是把它看成有结构、有组织的数据,而另一种则把它看成线性空间的数据

Linux内核源代码情景分析系列的更多相关文章

  1. Linux内核源代码情景分析-fork()

    父进程fork子进程: child = fork() fork经过系统调用.来到了sys_fork.具体过程请參考Linux内核源码情景分析-系统调用. asmlinkage int sys_fork ...

  2. linux 内核源代码情景分析——地址映射的全过程

    linux 内核采用页式存储管理.虚拟地址空间划分成固定大小的"页面",由MMU在运行时将虚拟地址映射成某个物理内存页面中的地址.页式内存管理比段式内存管理有很多好处,但是由于In ...

  3. linux 内核源代码情景分析——linux 内存管理的基本框架

    386 CPU中的页式存管的基本思路是:通过页面目录和页面表分两个层次实现从线性地址到物理地址的映射.这种映射模式在大多数情况下可以节省页面表所占用的空间.因为大多数进程不会用到整个虚存空间,在虚存空 ...

  4. linux 内核源代码情景分析——linux 内核源代码中的C语言代码

    linux 内核的主体是以GNU的C语言编写的,GNU为此提供了编译工具gcc.GNU对C语言本身作了不少扩充. 1) gcc 从 C++ 语言中吸收了"inline"和" ...

  5. linux 内核源代码情景分析——Intel X86 CPU 系列的寻址方式

    当我们说一个CPU是"16位"或"32"位时,指的是处理器中"算数逻辑单元"(ALU)的宽度.数据总线通常与ALU具有相同的宽度.当Inte ...

  6. linux 内核源代码情景分析——用户堆栈的扩展

    上一节中,我们浏览了一次因越界访问而造成映射失败从而引起进程流产的过程,不过有时候,越界访问时正常的.现在我们就来看看当用户堆栈过小,但是因越界访问而"因祸得福"得以伸展的情景. ...

  7. Linux内核源代码情景分析-中断半

    一.中断初始化 1.中断向量表IDT初始化 void __init init_IRQ(void) { int i; #ifndef CONFIG_X86_VISWS_APIC init_ISA_irq ...

  8. linux 内核源代码情景分析——越界访问

    页式存储管理机制通过页面目录和页面表将每个线性地址转换成物理地址,当遇到下面几种情况就会使CPU产生一次缺页中断,从而执行预定的页面异常处理程序: ① 相应的页面目录或页表项为空,也就是该线性地址与物 ...

  9. linux 内核源代码情景分析——linux 内核源码中的汇编语言代码

    1. 用汇编语言编写部分核心代码的原因: ① 操作系统内核中的底层程序直接与硬件打交道,需要用到一些专用的指令,而这些指令在C语言中并无对应的语言成分: ② CPU中的一些特殊指令也没有对应的C语言成 ...

随机推荐

  1. C# 合并DLL, 合并DLL进入EXE 【转】

    使用方法非常简单 在项目属性窗口中,选择"生成事件",在"生成后事件命令行"下的文本框中输入 ilmerge /ndebug /t:dll /log c:/1/ ...

  2. uva 11992 - Fast Matrix Operations

    简单的线段树的题: 有两种方法写这个题,目前用的熟是这种慢点的: 不过不知道怎么老是T: 感觉网上A过的人的时间度都好小,但他们都是用数组实现的 难道是指针比数组慢? 好吧,以后多用数组写写吧! 超时 ...

  3. Stanford CoreNLP--Named Entities Recognizer(NER)

    Standford Named Entities Recognizer(NER),命名实体识别是信息提取(Information Extraction)的一个子任务,它把文字的原子元素(Atomic ...

  4. 【HDU 1133】 Buy the Ticket (卡特兰数)

    Buy the Ticket Problem Description The "Harry Potter and the Goblet of Fire" will be on sh ...

  5. ANDROID_MARS学习笔记_S04_004_用HTTPCLENT发带参数的get和post请求

    一.代码 1.xml(1)activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/r ...

  6. ScrollView中嵌套两个ListView

    做的项目中要使用两个ListView在同一个页面上下显示,因为数据源不同,不能通过在Adapter中设置标志位去区分显示,最后只能硬着头皮做一个ScrollView嵌套两个ListView,但按正常情 ...

  7. Chrome 浏览器地址栏直接搜索太慢的解决方案

    用Chrome经常直接把要搜索的内容写在地址栏, 回国就搜索,但最近发现搜索结果出来得太慢,要刷新好几次才行. 解决方案如下: 打开Chrome的"设置", 找到”管理搜索引擎“, ...

  8. A. Difference Row

    A. Difference Row time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...

  9. Modifying the ASP.NET Request Queue Limit

    Modifying the ASP.NET Request Queue Limit When ASP.NET is queried, the request for service is carrie ...

  10. JavaScript高级程序设计28.pdf

    classList属性 在操作类名时需要通过className属性添加.删除和替换类名 <div class="bd user disabled">...</di ...