Linux文件系统(四)---三大缓冲区之inode缓冲区 (内存inode映像 )

在文件系统中,有三大缓冲为了提升效率:inode缓冲区、dentry缓冲区、块缓冲。

(内核:2.4.37)

一、inode缓冲区

为了加快对索引节点的索引,引入inode缓冲区,下面我们看Linux/fs/inode.c代码。inode缓冲区代码

1、一些数据结构:

之前已经说过,有多个链表用于管理inode节点:

<span style="font-size:14px;">59 static LIST_HEAD(inode_in_use);
 60 static LIST_HEAD(inode_unused);
 61 static LIST_HEAD(inode_unused_pagecache);
 62 static struct list_head *inode_hashtable;
 63 static LIST_HEAD(anon_hash_chain); /* for inodes with NULL i_sb */</span>

inode_in_use:正在使用的inode,即有效的inode,i_count > 0且i_nlink > 0。

inode_unused:有效的节点,但是还没有使用,处于空闲状态。(数据不在pagecache中)。

inode_unused_pagecache:同上。(数据在pagecache中)。

inode_hashtable:用于inode在hash表中,提高查找效率。

anon_hash_chain:用于超级块是空的的inodes。例如:sock_alloc()函数, 通过调用fs/inode.c中get_empty_inode()创建的套接字是一个匿名索引节点,这个节点就加入到了anon_hash_chain链表。

dirty:用于保存超级块中的所有的已经修改的inodes。

<span style="font-size:14px;"> 76 struct inodes_stat_t inodes_stat;
 77
 78 static kmem_cache_t * inode_cachep;</span>

上面的两个字段:

inodes_stat:记录inodes节点的状态。

inode_cachep:对inodes对象的缓存块。

2、基本初始化:初始化inode哈希表头和slab内存缓存块

索引节点高速缓存的初始化是由inode_init()实现的,现在看看下面代码:

<span style="font-size:14px;">1296 /*
1297  * Initialize the hash tables.
1298  */
1299 void __init inode_init(unsigned long mempages)   /* 参数:表示inode缓存使用的物理页面数 */
1300 {
1301         struct list_head *head;
1302         unsigned long order;
1303         unsigned int nr_hash;
1304         int i;
1305         /* 下面的一段操作就是根据PAGE_SHIFT,PAGE_SIZE给hash表分配空间 */
1306         mempages >>= (14 - PAGE_SHIFT);
1307         mempages *= sizeof(struct list_head);
1308         for (order = 0; ((1UL << order) << PAGE_SHIFT) < mempages; order++)
1309                 ;
1310
1311         do {
1312                 unsigned long tmp;
1313
1314                 nr_hash = (1UL << order) * PAGE_SIZE /
1315                         sizeof(struct list_head);
1316                 i_hash_mask = (nr_hash - 1);
1317
1318                 tmp = nr_hash;
1319                 i_hash_shift = 0;
1320                 while ((tmp >>= 1UL) != 0UL)
1321                         i_hash_shift++;
1322                 /* inode_hashtable是一个全局变量,用于hash表,上面说过,需要预定order页的内存作为inode-hash表使用 */
1323                 inode_hashtable = (struct list_head *)
1324                         __get_free_pages(GFP_ATOMIC, order);
1325         } while (inode_hashtable == NULL && --order >= 0);
1326
1327         printk(KERN_INFO "Inode cache hash table entries: %d (order: %ld, %ld bytes)\n",
1328                         nr_hash, order, (PAGE_SIZE << order));
1329         /* 如果分配不成功就失败 */
1330         if (!inode_hashtable)
1331                 panic("Failed to allocate inode hash table\n");
1332         /* 下面就是初始化每个inde-hash节点 */
1333         head = inode_hashtable;
1334         i = nr_hash;
1335         do {
1336                 INIT_LIST_HEAD(head);
1337                 head++;
1338                 i--;
1339         } while (i);
1340
1341         /* inode slab cache:创建一个inode的slab缓存,以后的inode缓存都从这个slab中进行分配 */
1342         inode_cachep = kmem_cache_create("inode_cache", sizeof(struct inode),
1343                                          0, SLAB_HWCACHE_ALIGN, init_once,
1344                                          NULL);
1345         if (!inode_cachep)
1346                 panic("cannot create inode slab cache");
1347
1348         unused_inodes_flush_task.routine = try_to_sync_unused_inodes;
1349 }
1350</span>

注意上面的逻辑,说明两个问题:

1).  第一初始化inode_hashtable作为链表的头。

2).  初始化inode的slab缓存,也就是说,如果我需要分配一个inode缓存在内存中,那么都从这个inode_cachep中分配一个inode内存节点。然后统一加入到这个inode_hashtable中进行管理!也就是所谓的创建inode slab分配器缓存。

下面看看具体的缓存的分配过程:

先看init_once函数:

<span style="font-size:14px;">169 static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
170 {
171         struct inode * inode = (struct inode *) foo;
172
173         if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
174             SLAB_CTOR_CONSTRUCTOR)
175                 inode_init_once(inode);
176 }</span>

注意:在上面的kmem_cache_create函数中,执行的顺序是:

---> kmem_cache_create(里面重要的一步是cachep->ctor = ctor; cachep->dtor = dtor;)

---> kmem_cache_alloc

---> __kmem_cache_alloc

---> kmem_cache_grow(里面一个重要设置是:ctor_flags = SLAB_CTOR_CONSTRUCTOR;)

---> kmem_cache_init_objs:里面会执行cachep->ctor(objp, cachep, ctor_flags);

这样最终就跳转到上面的init_once函数中了!在init函数中执行的是inode_init_once函数:

<span style="font-size:14px;">141 /*
142  * These are initializations that only need to be done
143  * once, because the fields are idempotent across use
144  * of the inode, so let the slab aware of that.
145  */
146 void inode_init_once(struct inode *inode)
147 {
148         memset(inode, 0, sizeof(*inode));
149         __inode_init_once(inode);
150 }</span>

再看__inode_init_once函数:

<span style="font-size:14px;">152 void __inode_init_once(struct inode *inode)
153 {
154         init_waitqueue_head(&inode->i_wait);
155         INIT_LIST_HEAD(&inode->i_hash);
156         INIT_LIST_HEAD(&inode->i_data.clean_pages);
157         INIT_LIST_HEAD(&inode->i_data.dirty_pages);
158         INIT_LIST_HEAD(&inode->i_data.locked_pages);
159         INIT_LIST_HEAD(&inode->i_dentry);
160         INIT_LIST_HEAD(&inode->i_dirty_buffers);
161         INIT_LIST_HEAD(&inode->i_dirty_data_buffers);
162         INIT_LIST_HEAD(&inode->i_devices);
163         sema_init(&inode->i_sem, 1);
164         sema_init(&inode->i_zombie, 1);
165         init_rwsem(&inode->i_alloc_sem);
166         spin_lock_init(&inode->i_data.i_shared_lock);
167 }</span>

3、注意知道现在我们主要说了上面的两个基本的问题(红字部分),但是这只是一个框架而已,对于具体的一个文件系统来说怎么个流程,下面需要看看!

我们以最常见的ext2作为说明:

现在一个ext2类型的文件系统想要创建一个inode,那么执行:ext2_new_inode函数

<span style="font-size:14px;">314 struct inode * ext2_new_inode (const struct inode * dir, int mode)
315 {
316         struct super_block * sb;
317         struct buffer_head * bh;
318         struct buffer_head * bh2;
319         int group, i;
320         ino_t ino;
321         struct inode * inode;
322         struct ext2_group_desc * desc;
323         struct ext2_super_block * es;
324         int err;
325
326         sb = dir->i_sb;
327         inode = new_inode(sb);   /* 创建一个inode节点,这个函数就是在fs/inode.c中的new_inode函数 */
328         if (!inode)
329                 return ERR_PTR(-ENOMEM);
330
331         lock_super (sb);
332         es = sb->u.ext2_sb.s_es;
333 repeat:
334         if (S_ISDIR(mode))
335                 group = find_group_dir(sb, dir->u.ext2_i.i_block_group);
336         else
337                 group = find_group_other(sb, dir->u.ext2_i.i_block_group);
338
339         err = -ENOSPC;
340         if (group == -1)
341                 goto fail;
342
343         err = -EIO;
344         bh = load_inode_bitmap (sb, group);
345         if (IS_ERR(bh))
346                 goto fail2;
347
348         i = ext2_find_first_zero_bit ((unsigned long *) bh->b_data,
349                                       EXT2_INODES_PER_GROUP(sb));
350         if (i >= EXT2_INODES_PER_GROUP(sb))
351                 goto bad_count;
352         ext2_set_bit (i, bh->b_data);
353
354         mark_buffer_dirty(bh);
355         if (sb->s_flags & MS_SYNCHRONOUS) {
356                 ll_rw_block (WRITE, 1, &bh);
357                 wait_on_buffer (bh);
358         }
359
360         ino = group * EXT2_INODES_PER_GROUP(sb) + i + 1;
361         if (ino < EXT2_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) {
362                 ext2_error (sb, "ext2_new_inode",
363                             "reserved inode or inode > inodes count - "
364                             "block_group = %d,inode=%ld", group, ino);
365                 err = -EIO;
366                 goto fail2;
367         }
368
369         es->s_free_inodes_count =
370                 cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1);
371         mark_buffer_dirty(sb->u.ext2_sb.s_sbh);
372         sb->s_dirt = 1;
373         inode->i_uid = current->fsuid;
374         if (test_opt (sb, GRPID))
375                 inode->i_gid = dir->i_gid;
376         else if (dir->i_mode & S_ISGID) {
377                 inode->i_gid = dir->i_gid;
378                 if (S_ISDIR(mode))
379                         mode |= S_ISGID;
380         } else
381                 inode->i_gid = current->fsgid;
382         inode->i_mode = mode;
383
384         inode->i_ino = ino;
385         inode->i_blksize = PAGE_SIZE;   /* This is the optimal IO size (for stat), not the fs block size */
386         inode->i_blocks = 0;
387         inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
388         inode->u.ext2_i.i_state = EXT2_STATE_NEW;
389         inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags & ~EXT2_BTREE_FL;
390         if (S_ISLNK(mode))
391                 inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL);
392         inode->u.ext2_i.i_block_group = group;
393         ext2_set_inode_flags(inode);
394         insert_inode_hash(inode);           /* 将这个新的inode内存节点挂在hashtable中,这个函数在fs/inode.c中的insert_inode_hash函数 */
395         inode->i_generation = event++;
396         mark_inode_dirty(inode);
397
398         unlock_super (sb);
399         if(DQUOT_ALLOC_INODE(inode)) {
400                 DQUOT_DROP(inode);
401                 inode->i_flags |= S_NOQUOTA;
402                 inode->i_nlink = 0;
403                 iput(inode);
404                 return ERR_PTR(-EDQUOT);
405         }
406         ext2_debug ("allocating inode %lu\n", inode->i_ino);
407         return inode;
408
409 fail2:
410         desc = ext2_get_group_desc (sb, group, &bh2);
411         desc->bg_free_inodes_count =
412                 cpu_to_le16(le16_to_cpu(desc->bg_free_inodes_count) + 1);
413         if (S_ISDIR(mode))
414                 desc->bg_used_dirs_count =
415                         cpu_to_le16(le16_to_cpu(desc->bg_used_dirs_count) - 1);
416         mark_buffer_dirty(bh2);
417 fail:
418         unlock_super(sb);
419         make_bad_inode(inode);
420         iput(inode);
421         return ERR_PTR(err);
422
423 bad_count:
424         ext2_error (sb, "ext2_new_inode",
425                     "Free inodes count corrupted in group %d",
426                     group);
427         /* Is it really ENOSPC? */
428         err = -ENOSPC;
429         if (sb->s_flags & MS_RDONLY)
430                 goto fail;
431
432         desc = ext2_get_group_desc (sb, group, &bh2);
433         desc->bg_free_inodes_count = 0;
434         mark_buffer_dirty(bh2);
435         goto repeat;
436 }</span>

这个函数比较复杂,但是我们主要看327行和394行,就是创建一个inode内存节点,然后将这个inode插入inode_hashtable中!

这个函数具体的解释不再看了,现在主要从这两个函数入手:

1). fs/inode.c中的new_inode函数,创建一个inode内存节点:

<span style="font-size:14px;">964 struct inode * new_inode(struct super_block *sb)
965 {
966         static unsigned long last_ino;
967         struct inode * inode;
968
969         spin_lock_prefetch(&inode_lock);
970
971         inode = alloc_inode(sb);/* 这个是主要的分配函数 */
972         if (inode) {
973                 spin_lock(&inode_lock);
974                 inodes_stat.nr_inodes++;  /* inode_stat是一个所有节点状态字段,这里表明增加了一个新的inode */
975                 list_add(&inode->i_list, &inode_in_use); /* 将这个inode加入到正在使用的链表中:inode_use链表 */
976                 inode->i_ino = ++last_ino; /* 给这个inode分配一个inode号! */
977                 inode->i_state = 0;
978                 spin_unlock(&inode_lock);
979         }
980         return inode;
981 }</span>

看看这个alloc_inode函数:

<span style="font-size:14px;"> 80 static struct inode *alloc_inode(struct super_block *sb)
 81 {
 82         static struct address_space_operations empty_aops;
 83         static struct inode_operations empty_iops;
 84         static struct file_operations empty_fops;
 85         struct inode *inode;
 86
 87         if (sb->s_op->alloc_inode)   /* 如果提供了自己的分配函数,那么这个文件系统自己分配去~~~,具体不多说 */
 88                 inode = sb->s_op->alloc_inode(sb);
 89         else {
 90                 inode = (struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL);/* 这个就是通用的分配函数,从我们初始化好的inode_cache中分配 */
 91                 /* will die */
 92                 if (inode)
 93                         memset(&inode->u, 0, sizeof(inode->u));
 94         }
 95         /* 下面初始化的东西就不多说了 */
 96         if (inode) {
 97                 struct address_space * const mapping = &inode->i_data;
 98
 99                 inode->i_sb = sb;
100                 inode->i_dev = sb->s_dev;
101                 inode->i_blkbits = sb->s_blocksize_bits;
102                 inode->i_flags = 0;
103                 atomic_set(&inode->i_count, 1);
104                 inode->i_sock = 0;
105                 inode->i_op = &empty_iops;
106                 inode->i_fop = &empty_fops;
107                 inode->i_nlink = 1;
108                 atomic_set(&inode->i_writecount, 0);
109                 inode->i_size = 0;
110                 inode->i_blocks = 0;
111                 inode->i_bytes = 0;
112                 inode->i_generation = 0;
113                 memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
114                 inode->i_pipe = NULL;
115                 inode->i_bdev = NULL;
116                 inode->i_cdev = NULL;
117
118                 mapping->a_ops = &empty_aops;
119                 mapping->host = inode;
120                 mapping->gfp_mask = GFP_HIGHUSER;
121                 inode->i_mapping = mapping;
122         }
123         return inode;
124 }</span>

我们主要看87行和90行!看了注释也就明白了!第一种是文件系统也就是这个超级快提供了分配函数,那么就这个文件系统按照自己的意愿去分配,如果没有,那么就是要用这个通用的分配函数inode = (struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL);这个函数其实很简单,其实就是在我们已经初始化好的这个inode_cache中分配一个inode内存块出来。

2). fs/inode.c中的insert_inode_hash函数,将新的分配的inode插入到inode_hashtable中:

<span style="font-size:14px;">1166 void insert_inode_hash(struct inode *inode)
1167 {
1168         struct list_head *head = &anon_hash_chain;  /* anon_hash_chain是代表没有超级块的inode链表(有些临时的inode无需超级块) */
1169         if (inode->i_sb)
1170                 head = inode_hashtable + hash(inode->i_sb, inode->i_ino); /* 这个是正常的插入 */
1171         spin_lock(&inode_lock);
1172         list_add(&inode->i_hash, head);
1173         spin_unlock(&inode_lock);
1174 }</span>

注意这个hash表其实就可以看做是一个数组链表组合体,如图所示:

head = inode_hashtable + hash(inode->i_sb, inode->i_ino);这一行就是通过这个hash函数算出hash值,找到这个inode应该放在哪一列。譬如定位到第三列,那么第三列中的都是hash值相同的inode。然后所有的这列inode都是构成双向链表的。注意inode中的i_hash字段就做这个事的!!list_add(&inode->i_hash, head);函数就是将hash值相同的inode构成双向链表。

看一下这个具体的hash函数(inode.c中):

<span style="font-size:14px;">1043 static inline unsigned long hash(struct super_block *sb, unsigned long i_ino)
1044 {
1045         unsigned long tmp = i_ino + ((unsigned long) sb / L1_CACHE_BYTES);
1046         tmp = tmp + (tmp >> I_HASHBITS);
1047         return tmp & I_HASHMASK;
1048 }</span>

OK,上面的具体的inode创建和加入的流程基本清楚了。具体创建的过程是涉及到内存这一块的,不多说了。

4. 下面看看给一个怎么去找到一个inode,涉及ilookup函数:

<span style="font-size:14px;">1102 struct inode *ilookup(struct super_block *sb, unsigned long ino)
1103 {
1104         struct list_head * head = inode_hashtable + hash(sb,ino);/* 获得hash值 */
1105         struct inode * inode;
1106
1107         spin_lock(&inode_lock);
1108         inode = find_inode(sb, ino, head, NULL, NULL);  /* 寻找inode */
1109         if (inode) {
1110                 __iget(inode);
1111                 spin_unlock(&inode_lock);
1112                 wait_on_inode(inode);
1113                 return inode;
1114         }
1115         spin_unlock(&inode_lock);
1116
1117         return inode;
1118 }</span>

这个函数其实比较简单了,首先还是获得这个inode的hash值定位,然后开始finde_inode:

<span style="font-size:14px;">929 static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)
930 {
931         struct list_head *tmp;
932         struct inode * inode;
933
934 repeat:
935         tmp = head;
936         for (;;) {
937                 tmp = tmp->next;
938                 inode = NULL;
939                 if (tmp == head)   /*双向循环链表结束条件*/
940                         break;
941                 inode = list_entry(tmp, struct inode, i_hash); /*获得链表中一个inode*/
942                 if (inode->i_ino != ino) /*是否找到*/
943                         continue;
944                 if (inode->i_sb != sb) /*是否合理:是不是我需要的super_block中的inode*/
945                         continue;
946                 if (find_actor && !find_actor(inode, ino, opaque)) /*这个是一个查找函数指针,用户定义的一些规则是否满足*/
947                         continue;
948                 if (inode->i_state & (I_FREEING|I_CLEAR)) { /*注意inode节点的状态如果是free或者clear,那么等free之后再重新找*/
949                         __wait_on_freeing_inode(inode);
950                         goto repeat;
951                 }
952                 break;
953         }
954         return inode; /*返回找到的inode节点*/
955 }</span>

上面函数最核心的本质不就是双向链表的查找么,OK。

最后:关于inode怎么工作的,将会在后面的分析ext2代码中在详细研究。

时间: 2024-10-12 17:32:12

Linux文件系统(四)---三大缓冲区之inode缓冲区 (内存inode映像 )的相关文章

Linux文件系统(五)---三大缓冲区之buffer块缓冲区

在文件系统中,有三大缓冲为了提升效率:inode缓冲区.dentry缓冲区.块缓冲. (内核:2.4.37) 二.块buffer缓冲区 0.整体来说,Linux 文件缓冲区分为page cache和buffer cache,每一个 page cache 包含若干 buffer cache. > 内存管理系统和 VFS 只与 page cache 交互,内存管理系统负责维护每项 page cache 的分配和回收,同时在使用"内存映射"方式访问时负责建立映射. > VFS 负

10. Linux文件系统

1. Linux文件系统有三部分组成:文件名,inode,block(真正存取数据) 可以使用: stat 命令查看文件Inode信息 [[email protected] etc]# stat /etc/passwd File: "/etc/passwd" Size: 1653 Blocks: 8 IO Block: 4096 普通文件 Device: 802h/2050d Inode: 9177 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0

Linux文件系统详解(文件系统层次、分类、存储结构、存储介质、文件节点inode)

从操作系统的角度详解Linux文件系统层次.文件系统分类.文件系统的存储结构.不同存储介质的区别(RAM.ROM.Flash).存储节点inode. 本文参考:http://blog.chinaunix.net/uid-8698570-id-1763151.html http://www.iteye.com/topic/816268 http://soft.chinabyte.com/os/142/12315142.shtml http://www.ibm.com/developerworks/

Linux 文件系统---虚拟文件系统VFS----超级块、inode、dentry、file

一: 什么是文件系统,详见:http://zh.wikipedia.org/zh/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F 其实一句话就是管理这块文件的机制(组织方式,数据结构之类...) Linux系统中存在很多的文件系统,例如常见的ext2,ext3,ext4,sysfs,rootfs,proc...很多很多...我们知道每个文件系统是独立的,有自己的组织方法,操作方法.那么对于用户来说,不可能所有的文件系统都了解,那么怎么做到让用户透明的去处理文件呢?例如:

理解Linux文件系统之inode

很少转发别人的文章,但是这篇写的太好了. 理解inode 作者: 阮一峰 inode是一个重要概念,是理解Unix/Linux文件系统和硬盘储存的基础. 我觉得,理解inode,不仅有助于提高系统操作水平,还有助于体会Unix设计哲学,即如何把底层的复杂性抽象成一个简单概念,从而大大简化用户接口. 下面就是我的inode学习笔记,尽量保持简单. =================================== 理解inode 作者:阮一峰 一.inode是什么? 理解inode,要从文件储

Linux文件系统之inode与软硬连接

一.inode是什么? 理解inode,要从文件储存说起. 文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector).每个扇区储存512字节(相当于0.5KB). 操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block).这种由多个扇区组成的"块",是文件存取的最小单位."块"的大小,最常见的是4KB,即连续八个 sector组成一个 block. 文

Linux文件系统中的inode节点详细介绍

这篇文章主要介绍了Linux文件系统中的inode节点,详细讲解了inode是什么.inode包含的信息.inode号码的相关资料等. 一.inode是什么? 理解inode,要从文件储存说起.文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector).每个扇区储存512字节(相当于0.5KB). 操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block).这种由多个扇区组成的"块&q

Linux文件系统与索引节点inode浅析

我们知道磁盘在分区后还需要进行格式化,之后操作系统才能使用这个分区.而需要格式化的原因正是因为每种操作系统所设置的文件属性/权限并不相同.在Linux中,传统的磁盘文件系统是EXT,下面以EXT为例浅析Linux的文件系统与索引节点. 我们知道,文件的数据除了文件实际内容之外,通常还含有非常多的属性.文件系统通常会将权限与属性等数据放置到inode(index node)中,至于实际数据则放置到data block块中:另外,还有一个超级块会记录整个文件系统的整体信息. 在文件系统中,每个ino

深入理解Linux文件系统基础inode

转载自:http://www.ruanyifeng.com/blog/2011/12/inode.html 这篇文章对Inode的讲解比较透彻,好文章分享一下,一是收藏,二是让大家也可以一起学习(已懂的略过),对于一些重点的本人标红,并加些个人的补充. 一.inode是什么? 理解inode,要从文件储存说起. 文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector).每个扇区储存512字节(相当于0.5KB). 操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是