Chinaunix首页 | 论坛 | 博客
  • 博客访问: 10202708
  • 博文数量: 1669
  • 博客积分: 16831
  • 博客等级: 上将
  • 技术积分: 12594
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-25 07:23
个人简介

柔中带刚,刚中带柔,淫荡中富含柔和,刚猛中荡漾风骚,无坚不摧,无孔不入!

文章分类

全部博文(1669)

文章存档

2023年(4)

2022年(1)

2021年(10)

2020年(24)

2019年(4)

2018年(19)

2017年(66)

2016年(60)

2015年(49)

2014年(201)

2013年(221)

2012年(638)

2011年(372)

分类: LINUX

2012-12-10 12:57:42

Linux中的inode简介  

2012-12-10 10:52:09|  分类: linux文件系统 |  标签: |字号 

前几天上机的时候遇见了一件奇怪的事情,我将320GB的移动硬盘(3个分区,1个为FAT32格式,2个为NTFS格式)挂载到RHEL5中,准备复制36GB的数据,却发现FAT32分区的可用空间只有20GB,记得自己刚刚清理了硬盘,应该至少还有40GB的空间啊。卸载硬盘后拿到Windows环境下查看,果真还有40GB。很是奇怪,后来想先复制一下试一试,结果到了20GB的时候就提示空间已满,更奇怪的是将刚复制的文件转移走后,在RHEL下df -h查看空间,居然可用空间仍然为0!

上网搜寻,却原来是inode的缘故。磁盘在分区格式化的时候inode已经分配好,总的数目是一定的。如果将磁盘分区比做一本书的话,文件的索引inode就如同目录,而文件的内容就如同书的正文部分。需要注意的是inode是不可再分配的(ext4除外,它可以动态分配inode),如果目录用光了,则即使仍有空白页,也无法继续存储数据。

1. inode 是作甚么的?

一个 filesystem 可以粗略地分成 inode table 与 data area 两部份.
inode table 上有许多的 inode, 每个 inode 分别 记录一个档案的属性,
与这个档案分布在哪些 datablock 上

2. 一个 inode 有多大呢?

128 byte!

3. inode 和 data area 的关系

在 new filesystem 时, 通常会有一个参数, 用来描述要分配多少比例的空间给
inode table. 举例来说,

newfs -i 2048

是指 file system 中, 每分配 2048 byte 给 data area, 就分配一个 inode
但是一个 inode 就并不是一定就用掉 2048 byte, 也不是说 files allocation
的最小单位是 2048 byte, 它仅仅只是代表 filesystem 中
inode table/data area 分配空间的比例是 128/2048 也就是 1/16
(换个角度想, 我们可以想成是预估 filesystem 中 file 平均大小是 2048 byte)

如果 inode table 太小, 那么在每个档案都很小的时候, 就会发生 inode 用光 而
datablock 还剩一堆的情形.

4. file allocation 的最小单位 和 inode 多少有没有关系呢?

没有关系!

FFS 中真正的最小单位是 fragment size 也就是我们在 new filesystem 时用的

newfs -b 8192 -f 1024

ps: -b 8192 代表 blocksize=8192, 这种"较大单位"是用来加速大档案的存取用的

在 FreeBSD 中, 内定的是 -i 4096 -b 8192 -f 1024. 如果您要架 bbs/new 的话
可以考虑用 -i 1024 -b4096 -f1024

5、inode的分类及其具体涵义
  inode分为内存中的inode和文件系统中的inode,为了避免混淆,我们称前者为VFS inode, 而后者以EXT2为代表,我们称为Ext2 inode。

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

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

 (3)、对VFS inode与ext2 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结构。

6、inode的分配 (对应ext2 inode)
  1、每个inode的大小为128个字节,下面具体介绍哪几个字节代表什么内容。
  0~1 表示文件的类型和属性(2 bytes)0x41ed (rwxr-xr-x) 0x8180 (-rw-------)
  2~3 表示文件的长度(2 bytes)
  4~7 表示文件宿主用户id(4 bytes)
  8~19 表示三个时间属性,每4个字节代表一个时间,分别是atime,ctime,mtime(12B)
  20~23 表示Deletion Time 即删除的时间。
  24~25 表示用户组id (2 bytes)
  26~27 表示硬链接的个数。
  28~31 表示Blockcount 的大小
  32~39 暂不清楚??
  40~99 这60个字节就是inode中的15个指针,每个指针占4个字节。
  100~103 暂不清楚??
  104~107 表示文件访问控制表号(File ACL)
  108~111 表示(Directory ACL)
  112~127 暂不清楚??
  2、在分割扇区时,系统会先做出一堆inode以供以后使用,inode 的数量关系着系统中可以建立的档案及目录总数。有时候,这些inode或许会不够用,例如当一个程序产生大量小文件的时候,此时文件系统就需要增加索引节点。
  同样,如果我们事先知道此文件系统只用来存放少数大文件,我们就可以通过减少索引节点的数目来达到节省磁盘空间的目的——毕竟每个索引节点占用128字节。
  创建文件系统的时候,可以使用newfs命令的-i选项来增加或者减少索引节点的数目。
  3、newfs -i 2048 -b 8192 -f 1024
  newfs创建新的文件系统
  -i 2048更改inode每2KB创建一个
  -b 8192设置block size的大小为8kB
  -f 1024设置fragments的大小为1KB

7、inode与ln (对于ext2 inode)
  1、当我们用ls 查看某个目录或文件时,如果加上-i 参数,就可以看到inode节点了;
  [root@localhost ~]# ls -li lsfile.sh
  2408949 -rwxr-xr-x 1 root root 7 04-21 12:47 lsfile.sh
  可见lsfile.sh 的inode值是2408949 ;
  2、创建硬链接,硬链接和源文件关系;
  用ln创建文件硬链接的语法:
  [root@localhost ~]#ln 源文件 目标文件
  下面我们举一个例子,在这个例子中,我们要为sun.txt创建其硬链接sun002.txt。然后看一下 sun.txt和sun002.txt的属性的变化;
  [root@localhost ~]# ls -li sun.txt 注:查看sun.txt的属性;
  2408263 -rw-r--r-- 1 root root 29 04-22 21:02 sun.txt 注:这是sun.txt的属性;
  [root@localhost ~]# ln sun.txt sun002.txt 注:我们通过ln 来创建sun.txt的硬链接文件sun002.txt
  [root@localhost ~]# ls -li sun* 注:我们列一下sun.txt 和sun002.txt
  2408263 -rw-r--r-- 2 root root 29 04-22 21:02 sun002.txt
  2408263 -rw-r--r-- 2 root root 29 04-22 21:02 sun.txt
  我们可以看到sun.txt在没有创建硬链接文件sun002.txt的时候,其链接个数是1(也就是-rw-r--r--后的那个数值),创建了硬链接 sun002.txt创建后,这个值变成了2。也就是说,我们每次为sun.txt创建一个新的硬链接文件后,其硬链接个数都会增加1。
  inode值相同的文件,他们的关系是互为硬链接的关系。当我们修改其中一个文件的内容时,互为硬链接的文件的内容也会跟着变化。如果我们删除互为硬链接关系的某个文件时,其它的文件并不受影响。比如我们把sun.txt删除后,我们还是一样能看到sun002.txt的内容,并且sun02.txt仍是存在的。
  可以这么理解,互为硬链接关系的文件,他们好像是克隆体,他们的属性几乎是完全一样;
  下面的例子,我们把sun.txt删除,然后我们看一下sun002.txt是不是能看到其内容。
  [root@localhost ~]# rm -rf sun.txt
  [root@localhost ~]# more sun002.txt
  注意:硬链接不能为目录创建,只有文件才能创建硬链接。
  3、软链接的创建,及软接与源文件的关系;
  创建软链接(也被称为符号链接)的语法;
  [root@localhost ~]# ln -s 源文文件或目录 目标文件或目录
  软链接也叫符号链接,他和硬链接有所不同,软链接文件只是其源文件的一个标记。当我们删除了源文件后,链接文件不能独立存在,虽然仍保留文件名,但我们却不能查看软链接文件的内容了。
  [root@localhost ~]# ls -li linuxsir001.txt
  2408274 -rw-r--r-- 1 root root 29 04-22 21:53 linuxsir001.txt
  [root@localhost ~]# ln -s linuxsir001.txt linuxsir002.txt
  [root@localhost ~]# ls -li linuxsir001.txt linuxsir002.txt
  2408274 -rw-r--r-- 1 root root 29 04-22 21:53 linuxsir001.txt
  2408795 lrwxrwxrwx 1 root root 15 04-22 21:54 linuxsir002.txt -> linuxsir001.txt
  上面的例子,首先我们查看 linuxsir001.txt 的属性,比如inode、所属文件种类、创建或修改时间等... ...我们来对比一下:
  首先 对比一下节点:两个文件的节点不同;
  其次 两个文件的归属的种类不同 linuxsir001.txt是-,也就是普通文件,而linuxsir002.txt 是l,它是一个链接文件;
  第三 两个文件的读写权限不同 linuxsir001.txt 是rw-r--r-- ,而linuxsir002.txt的读写权限是 rwxrwxrwx
  第三 两者的硬链接个数相同;都是1
  第四 两文件的属主和所归属的用户组相同;
  第五 修改(或访问、创建)时间不同;
  我们还注意到了linuxsir002.txt 后面有一个标记 ->,这表示linuxsir002.txt 是linuxsir001.txt的软链接文件。
  值得我们注意的是:当我们修改链接文件的内容时,就意味着我们在修改源文件的内容。当然源文件的属性也会发生改变,链接文件的属性并不会发生变化。当我们把源文件删除后,链接文件只存在一个文件名,因为失去了源文件,所以软链接文件也就不存在了。这一点和硬链接是不同的;
  [root@localhost ~]# rm -rf linuxsir001.txt 注:删除linuxsir001.txt
  [root@localhost ~]# ls -li linuxsir002.txt 注:查看linuxsir002 的属性;
  2408795 lrwxrwxrwx 1 root root 15 04-22 21:54 linuxsir002.txt -> linuxsir001.txt
  [root@localhost ~]# more linuxsir002.txt 注:查看linuxsir002.txt的内容;
  linuxsir002.txt: 没有那个文件或目录 注:得到提示,linuxsir002.txt不存在。
  上面的例子告诉我们,如果一个链接文件失去了源,就意味着他已经不存在了;
  我们可以看到软链接文件,其实只是源文件的一个标记,当源文件失去时,他也就是存在了。软链接文件只是占用了inode来存储软链接文件属性等信息,但文件存储是指向源文件的。
  软链接,可以为文件或目录都适用。无论是软链接还是硬链接,都可以用rm来删除。rm工具是通用的


文件系统的基础知识:
文件类型:
1.常规文件:计算机用户和操作系统用于存放数据、程序等信息的文件。一般分为文本文件和二进制文件。他们通常存储在外存储设备上。
2.目录文件:Linux文件系统将文件索引结点号和文件名同时保存在目录中,所以,目录文件就是将文件的名称和它的索引结点号结合在一起的一张表。
3.设备文件:系统中的各种I/O设备,存放在/dev目录中,通过mknod命令可以创建一个设备文件。通常设备文件分为字设备文件,比如键盘和鼠标等;还有块设备文件,比如硬盘和光驱等。
4.管道文件:主要用于在进程之间传递数据。某进程数据写入管道的一端,另一进程从管道另一端读取数据。通过系统调用pipe可以创建一个管道。管道文件在使用上和普通文件没有什么太大的区别,只不过这种文件不像普通文件那样存储与磁盘上,而是存储在内存中。
5.链接文件:又称符号链接文件,它提供了共享文件的一种方法,通过ln命令可以创建一个符号链接。
Linux文件系统结构:
/bin 二进制可执行命令,比如我们一般常用的命令ls,cat等。
/dev 设备特殊文件
/root 系统管理员的主目录
/etc 系统管理和配置文件
/home 用户主目录的基点,比如用户是hxu,主目录就是/home/hxu.
/lib 动态链接共享库.
/sbin 系统管理命令,这里存放的是系统管理员的使用的管理程序。
/tmp 共用的临时文件存储点
/mnt 用户临时安装其他文件系统的目录。
/proc 虚拟的目录,不占用磁盘空间,是系统内存的映射。可直接访问这个目录来获取系统信息。
/var 某些大文件的溢出区,例如各种服务的日志文件。
/usr 最庞大的目录,要用到的应用程序和文件几乎都在这个目录下。
虚拟文件系统:
为了保证Linux的开放性,设计人员必须考虑如何使用Linux除支持Ext2文件系统外,还能支持其他各种不同的文件的文件系统,比如NTFS,VFAT,JFS等。为此VFS就扮演了这样的角色。即支持跨越不同文件系统或存储设备的文件操作。
一个老师的博客中说了这句话我感觉挺形象的:Linux文件系统的魅力在于“成己” 亦“成人”。如果说整个Linux内核相当于一个“国”,那么,VFS 相当于一个“家”,且是一家之长,而各种具体的文件系统相当于“己”,或者说家庭中的一个个子女。在VFS统领的这个大家庭中,各个成员之所以能相互友好共存,在于其所具有共性和个性。
VFS第一词是虚拟,表示这样的文件系统在磁盘并没有对应的存储信息。这个“虚”VFS的信息都来源于“实”的文件系统,也就是说,VFS只管理挂载到系统中的实际文件系统。
VFS中主要管理以下4个主要对象:
(1)超级块对象:描述已安装文件系统。
(2)索引结点对象:描述一个文件。
(3)目录项对象:描述一个目录项,是路径的组成部分。
(4)文件对象:描述由进程打开的文件。


每个VFS对象都存放在一个适当的数据结构中,其中包括对象的属性和指定对象方法表的指针。内核可以动态地修改对象的方法,因此可以为对象建立专用的行为。下面我们来具体的看一下VFS中的数据结构。

超级块对象(super_block)
超级块用来描述整个文件系统的信息。对每个具体的文件系统来说,都有各自的超级块,如Ext2超级块,它们存放于磁盘上。当内核在对一个文件系统进行初始化和注册时在内存为其分配一个超级块,这就是VFS超级块。也就是说,VFS超级块是在文件系统安装时建立的,并在这些文件系统卸载时被自动删除,可见,VFS超级块只存在于内存中。看一下超级块在内核中的数据:

include/linux/fs.h

  1. struct super_block {
  2. struct list_head s_list; /* Keep this first */
  3. dev_t s_dev; /* search index; _not_ kdev_t */
  4. unsigned char s_dirt;
  5. unsigned char s_blocksize_bits;
  6. unsigned long s_blocksize;
  7. loff_t s_maxbytes; /* Max file size */
  8. struct file_system_type *s_type;
  9. const struct super_operations *s_op;
  10. const struct dquot_operations *dq_op;
  11. const struct quotactl_ops *s_qcop;
  12. const struct export_operations *s_export_op;
  13. unsigned long s_flags;
  14. unsigned long s_magic;
  15. struct dentry *s_root;
  16. struct rw_semaphore s_umount;
  17. struct mutex s_lock;
  18. int s_count;
  19. atomic_t s_active;
  20. struct list_head s_inodes;
  21. struct list_head files;
  22. struct list_head s_instances;
  23. ......
  24. }
s_list; 指向超级块的指针,超级块是个双向循环链表,通常我们通过list_entry宏来获取s_list所在超级块结构体的地址。超级块链表的头结点是变量super_blocks,sb_lock自旋锁保护链表免受多处理器系统上的同时访问。
s_dev; 具体文件系统的块设备标志符,比如,ext2文件系统所在设备为磁盘,则该设备号即为该磁盘在系统中的设备号。
s_dirt; 修改脏标识,超级块在内存中被修改后,该标志为1,则修改后的超级块必须写回磁盘.
s_blocksize_bits; 以位为单位的块大小。例如,如果块大小为1024字节,则该值为10.
s_blocksize; 以字节为单位的块大小,该值即代表这个块的具体大小.
s_maxbytes; 文件的最长长度。
*s_type; 指向具体文件系统的文件系统类型。
*s_op; 超级块方法。
*dq_op; 磁盘限额处理方法。
*s_qcop; 磁盘限额管理方法。
*s_export_op;网络文件系统使用的输出操作。
s_flags; 安装文件系统时的标志,记录比如只读或可读可写等这样的标志。
s_root:该文件系统根目录的目录项结构指针。利用该根目录项,可以访问到这个文件系统中的任何一个文件。
s_count:对该超级块的引用计数;
s_lock: 超级块信号量;
s_inodes:该文件系统中所有的索引结点形成一个双链表,该字段存放这个链表的头结点;
s_files:该文件系统中所有已被打开的文件形成一个双链表,该字段存放这个链表的头结点;
s_instances:某个具体文件系统中所有超级块会组成一个双链表。这个链表的头结点为super_block,头结点定义在该文件系统对应的file_system_type结构体中;
s_id[32]:文件系统的名称。比如ext3文件系统,该值为“ext3”;
与超级块关联的方法就是超级块操作,这些操作是super_operations来描述的,该结构的起始地址存放在超级块的s_op字段中。

include/linux/fs.h

  1. struct super_operations {
  2. struct inode *(*alloc_inode)(struct super_block *sb);
  3. void (*destroy_inode)(struct inode *);
  4. void (*dirty_inode) (struct inode *, int flags);
  5. int (*write_inode) (struct inode *, struct writeback_control *wbc);
  6. int (*drop_inode) (struct inode *);
  7. void (*evict_inode) (struct inode *);
  8. void (*put_super) (struct super_block *);
  9. void (*write_super) (struct super_block *);
  10. ......
  11. }
alloc_inode(sb):为索引结点分配空间,包括具体文件系统的的数据所需要的空间。
destroy_inode(inode):释放指定的索引结点;
write_inode(inode,flag):将指定的inode写回磁盘,用于指定inode的更新;flag参数表示I/O操作是否同步;
drop_inode(inode):移走指定的inode,与write_inode成对出现;
delete_inode(inode) : 在必须撤销索引结点时调用,删除内存中的VFS索引结点和磁盘上的文件数据及元数据。
put_super(super):释放指定的超级块,文件系统被卸载时使用;
write_super(super):如果该超级块被修改,即s_dirt为1时,则要将超级块写回磁盘,同时还要将s_dirt重设为0;

当文件系统需要对其所对应的超级块进行操作时,就应该使用超级块操作类中的具体函数。比如,定义sb为指向某个超级块的指针,如果该超级块需要将自己写回磁盘,则这样调用:sb->s_op->write_super(sb);

可以看到,虽然write_super函数是由sb所指的超级块所调用的,但是仍然将sb传递给write_super函数。

索引节点对象:
文件系统处理文件所需要的所有信息都存放在称为索引结点的 数据结构中,文件名可以随时更改,但是索引结点对文件是唯一的,并且随文件的存在而存在。具体文件系统的索引结点是存放在磁盘上的,是一种静态结构,要使用它。必须调用内存,填写VFS的索引结点。VFS的主要域定义如下:

include/linux/fs.h

  1. struct inode {
  2. struct hlist_node i_hash;
  3. struct list_head i_list; /* backing dev IO list */
  4. struct list_head i_dentry;
  5. unsigned long i_ino;
  6. atomic_t i_count;
  7. unsigned int i_nlink;
  8. uid_t i_uid;
  9. gid_t i_gid;
  10. dev_t i_rdev;
  11. u64 i_version;
  12. loff_t i_size;
  13. struct timespec i_atime;
  14. struct timespec i_mtime;
  15. struct timespec i_ctime;
  16. blkcnt_t i_blocks;
  17. unsigned short i_bytes;
  18. umode_t i_mode;
  19. const struct inode_operations *i_op;
  20. const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
  21. struct list_head i_devices;
  22. union {
  23. struct pipe_inode_info *i_pipe;
  24. struct block_device *i_bdev;
  25. };
  26. 。。。。。
  27. };
i_hash:为了提高查找正在被使用的inode的效率,每一个inode都会有一个hash值,所有hash值相同的inode形成一个双链表。该字段包含prev和next两个指针,分别指向上述链表的前一个元素和后一个元素;
i_list:VFS中使用四个链表来管理不同状态的inode结点。inode_unused将当前未使用的inode链接起来,inode_in_use将当前正在被使用的inode链接起来,超级块中的s_dirty将所有脏inode链接起来,i_hash将所有hash值相同的inode链接起来。i_list中包含prev和next两个指针,分别指向与当前inode处于同一个状态链表的前后两个元素。
i_sb_list:每个文件系统中的inode都会形成一个双联表,这个双链表的头结点存放在超级块的s_inodes中。而该字段中的prev和next指针分别指向在双链表中与其相邻的前后两个元素;
i_dentry:所有引用该inode的目录项将形成一个双联表,该字段即为这个双联表的头结点;
i_ino:索引结点号。通过ls -l命令可以查看文件的索引节点号;
i_count:引用计数;
i_nlink:硬链接数。当该inode描述一个目录时,这个值至少为2,代表.和..的数目;
i_uid:inode所属文件的拥有者的id,通过ls -n可查看拥有者id;
i_gid:inode所属文件所在组的id,通过ls -n可查看组id;
i_rdev:如果该inode描述的是一个设备文件,此值为设备号;
i_blkbits:以位为单位的块大小;
i_atime:文件最近一次被访问的时间。通过ls -lu可查看该时间;
i_mtime:文件最近一次被修改的时间,这里的修改只文件内容被修改。通过ls -l可查看该时间;
i_ctime:文件最近一次被修改的时间,这里的修改除了指文件内容被修改外,更强调的是文件的属性被修改。通过ls -lc可查看该时间;
i_blocks:文件使用块的个数,通过ls -s可以查看该某个文件的块使用数目;
i_mode:文件的访问权限;
i_op:指向索引结点操作结构体的指针;
i_fop:指向文件操作结构体的指针,这个字段用来初始化文件结构体(struct file)中的f_op字段;
i_sb:指向inode所属文件系统的超级块的指针;
i_pipe:如果inode所代表的文件是一个管道,则使用该字段;
i_bdev:如果inode所代表的文件是一个块设备,则使用该字段;
i_cdev:如果inode所代表的文件是一个字符设备,则使用该字段;
每个索引结点对象总是出现在下列双向循环链表的某个链表中(所有情况下,指向相邻元素的指针存放在i_list字段中):有效未使用的索引结点链表、正在使用的索引结点链表、脏索引结点的链表。
与索引结点对象相关联的方法叫索引结点操作:

include/linux/fs.h

  1. struct inode_operations {
  2. struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
  3. int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
  4. int (*link) (struct dentry *,struct inode *,struct dentry *);
  5. int (*unlink) (struct inode *,struct dentry *);
  6. int (*symlink) (struct inode *,struct dentry *,const char *);
  7. int (*mkdir) (struct inode *,struct dentry *,int);
  8. int (*rmdir) (struct inode *,struct dentry *);
  9. }
create():创建一个新的磁盘索引结点。
lookup():查诈一个索引结点所在的目录。
link():创建一个新的硬链接。
unlink():删除一个硬链接。
symlink():为符号链接创建一个新的索引结点。
mkdir():为目录项创建一个新的索引结点。

目录项对象:
每个文件除了有一个索引节点inode数据结构外,还有一个目录项dentry数据结构。dentry结构中有个d_inode指针指向相应的inode结构。大家有没有想过。既然inode结构和dentry结构都是对文件各方面属性的描述,为什么不直接把两个结构合二为一呢?这是因为目录项结构描述的是文件逻辑上的属性,在磁盘上并没有对应的映像,因此在dentry结构中不包含指出该对象已被修改的字段。目录项对象存放在名为dentry_cache的slab分配器高速缓存中;而indode结构代表的是物理意义上的文件,记录的是物理上的属性,对于一个具体的文件系统,如Ext2,Ext2_inode结构在磁盘上就有对应的映像。所以说,一个索引节点对象可能对应多个目录项对象。而一个目录项对象只能对应一个索引节点。也就是说,一个文件可以有不止一个文件名或路径名。这是因为一个应经建立的文件可以被Link到其他文件名。所以在inode结构中有一个队列i_dentry,凡是代表着同一个文件的所有目录项都通过其dentry结构中的d_alias域挂入相应inode结构中的i_dentry队列。
一旦目录项被读入内存,VFS就把他转换成基于dentry结构的一个目录项对象,对于进程查找路径名中的每个分量,内核都为其创建一个目录项对象;目录项对象将每个分量与其对应的索引结点相联系。例如:在查找路径名/proc/syskall时,内核为根目录“/”创建一个目录项对象,为根目录下的proc项创建一个第二级目录对象,为/proc目录下的test项创建一个第三级目录项对象。

include/linux/dcac

  1. struct dentry {
  2. unsigned int d_flags; 目录项高速缓存标志
  3. struct dentry *d_parent; 父目录的目录项对象
  4. struct qstr d_name; 文件名
  5. struct inode *d_inode; 与文件名关联的索引结点
  6. unsigned char d_iname[DNAME_INLINE_LEN]; 短文件名
  7. spinlock_t d_lock; 保护目录项的自旋锁
  8. const struct dentry_operations *d_op; 操作目录项的函数
  9. struct super_block *d_sb; 目录项树的根(即文件的超级块)
  10. struct list_head d_lru; 未使用的LRU链表
  11. struct list_head d_subdirs; 该目录项的子目录所形成的链表
  12. struct list_head d_alias; 索引结点别名的链表。
  13. .....
  14. };
一个文件系统中所有目录项结构一般会组织为一个哈希表,或者组织为一棵树,或者按照某种需要组织为一个链表,这是为文件访问和文件路径搜索奠定下良好的基础。对目录项操作的一组函数叫目录项操作表。
d_revalidata:判断目录项是否生效。
d_hash():生成一个哈希值。
d_compare():比较两个文件名。
d_delete():删除d_count域为0的目录项对象。
d_release():释放一个目录项对象。
d_input():调用该方法丢弃目录项对应的索引结点。

文件对象
文件对象是已打开文件在内存中的表示,因此,它在磁盘上并没有与之对应的数据。也就是说,文件对象只存在于内存中,所以这个结构也就不涉及脏数据字段和是否需要写回磁盘。
文件最终是要被进程访问的,一个进程可以打开多个文件,而一个文件可以被多个进程同时访问。每个打开的文件都用一个32位的数字来表示下一个读写的字节位置,这个数字叫做文间位置或偏移量。可以通过执行系统调用lseek对文件位置进行修改。Linux在file文件对象中保存了打来文件的文件位置,这个对象称为打开的文件描述符。
大家可能会产生疑惑,为什么不把文件位置存放在inode中呢?原因是Linux文件是共享的,加入把文件位置放在索引结点中,则如果有两个或更多的进程同时打开同一个文件,他们将去访问同一个索引结点,于是一个进程的lseek操作将影响到另一个进程的读操作,这显然是不可想象的。

inlcude/linux/fs.h

  1. struct file {
  2. struct list_head f_list; 所有打开的文件形成的链表
  3. struct dentry *f_dentry; 与文件相关的目录项对象
  4. struct vfsmount *f_vfsmnt; 该文件所在的已安装的文件系统
  5. struct file_operations *f_op; 指向文件操作表的指针
  6. unsigned int f_flags; 打开文件时所指定的标志
  7. fmode_t f_mode; 文件的打开模式
  8. loff_t f_pos; 文件的当前位置
  9. ......
  10. };
对文件操作的一组函数叫文件操作表。由file_operations结构描述:
llseek();修改文件指针
read():从文件中读出若干字节。
write():给文件中写若干字节。
mmap():文件到内存的映射。
open():打开文件。
flush():关闭文件时减少f_count的计数。
fsync():文件在缓冲区的数据写回磁盘。
fs_struct结构描述进程与文件系统的关系,该函数在include/linux/fs_struct.h中定义。


  1. struct fs_struct {
  2. int users;
  3. spinlock_t lock;
  4. seqcount_t seq;
  5. int umask;
  6. int in_exec;
  7. struct path root, pwd; root为根目录,pwd指向进程当前所在的目录。
  8. };




阅读(974) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~