分享

文件系统的缓存管理

 海漩涡 2016-03-28

文件系统的缓存管理

 

1     缓存的概念... 1

2     dentry缓存和inode缓存... 1

2.1    文件名查找... 1

2.2    dentry缓存... 2

2.3    dentry缓存的查找方式... 2

2.4    dentry缓存的管理... 2

2.5    超级块的dentry缓存... 2

2.6    dentry的数据结构... 2

2.7    inode缓存... 3

3     PageBuffer缓存... 3

3.1    缓存的作用... 3

3.2    缓存的种类... 3

3.3    附属在页缓存上的块缓存... 3

3.4    独立的块缓存... 4

3.5    /proc/meminfoBuffersCached的区别... 4

4     数据回写... 4

4.1    概念... 4

4.2    周期性写回... 5

4.3    完全同步... 5

4.4    强制写回... 5

4.5    当今最新进展... 5

5     proc文件系统... 5

5.1    概述... 5

5.2    proc中文件的读写方法... 6

6     通过proc文件系统管理缓存... 6

7     示例... 7

8     The Swap Cache. 7

 

1        缓存的概念

缓存位于客户和服务中间,用来加速访问。

常见的缓存有CPU缓存、浏览器缓存、代理服务器缓存等。

 

2        dentry缓存和inode缓存

2.1       文件名查找

如果想打开/usr/local/bin/xxx,首先要知道xxxinode,它保存在/usr/local/bin这个目录文件中,要打开/usr/local/bin这个文件,需要知道/usr/local/bininode,它的inode保存在/usr/local这个目录文件中,依次类推,直至查找“/”的inode。实际操作中,先打开磁盘中的/目录文件,查找/usrinode,然后打开磁盘中的/usr文件,查找/usr/localinode,依次如此这般。因此文件名查找需要反复查询inode表,并打开相应的目录文件读取inode

2.2       dentry缓存

在打开文件时,将需要的目录项都复制一份到内存中,下次需要读取相同的inode时,通过dentry缓存直接找到对应的inode

2.3       dentry缓存的查找方式

dentry的数据结构中有d_subdirs链表,链表元素可以连接到它的子目录和文件的dentry实例,搜索时沿着这个链表搜索。

2.4       dentry缓存的管理

内存中维护一个散列表(dentry_hashtable)包含了所有的dentry对象,还有一个LRU(最近最少使用,least recently used)链表,当某个dentry不再被使用时(使用计数d_count0),放进这个列表。新放进LRUdentry项置于链表的起始处,这表明越旧的dentry项就越靠后,在内核需要更多内存时,就会把一些比较旧的dentry释放掉。

LRU链表中的对象同时也处于散列表中,因此在需要时,也可通过散列表找到它,将其从LRU表中删除,同时将其使用计数加1

2.5       超级块的dentry缓存

在超级块的内存结构中,包含一个指针s_root,它指向内存中一个dentry实例。因此所有文件系统的根目录的dentry都在文件系统挂载的同时就加载到内存中了,相应的inode缓存也必然已经加载到内存中。

2.6       dentry的数据结构

struct dentry {

    atomic_t d_count;

    unsigned int d_flags;       /* protected by d_lock */

    spinlock_t d_lock;       /* per dentry lock */

    int d_mounted;

    struct inode *d_inode;      /* Where the name belongs to - NULL is

                   * negative */

    /*

     * The next three fields are touched by __d_lookup.  Place them here

     * so they all fit in a cache line.

     */

    struct hlist_node d_hash;   /* lookup hash list */

    struct dentry *d_parent; /* parent directory */

    struct qstr d_name;

 

    struct list_head d_lru;     /* LRU list */

    /*

     * d_child and d_rcu can share memory

     */

    union {

       struct list_head d_child;   /* child of parent list */

        struct rcu_head d_rcu;

    } d_u;

    struct list_head d_subdirs; /* our children */

    struct list_head d_alias;   /* inode alias list */

    unsigned long d_time;        /* used by d_revalidate */

    const struct dentry_operations *d_op;

    struct super_block *d_sb;   /* The root of the dentry tree */

    void *d_fsdata;          /* fs-specific data */

 

    unsigned char d_iname[DNAME_INLINE_LEN_MIN];  /* small names */

};

 

2.7       inode缓存

inode的内容保存在内存中

 

3        PageBuffer缓存

3.1       缓存的作用

缓存利用一部分系统物理内存,确保最重要、最常使用的块设备数据在操作时可以直接从内存中获取,而无需从低速设备中读取

数据在每次修改后并非都立即写回,同样是保存在写缓存中,在一定的时间间隔之后在回写。

3.2       缓存的种类

对低速设备的缓存有页缓存和块缓存两种。页缓存以内存页为单位,一般为4K;块缓存以磁盘块为单位,在进行I/O操作时,存取的单位是设备的各个块,而不是整个内存页。

页缓存占主要地位,因为内核中其它部分的操作都是以内存页为单位的;在以读取元数据块时,用块缓存更加方便。

块缓存分两种,一种是页缓存的附属,一种是独立的块缓存。

3.3       附属在页缓存上的块缓存

一些与块设备之间的传输操作,传输单位的长度依赖于底层设备的块长度,而内核的许多部分按页的粒度来执行I/O操作,在这种情况下,块缓存充当了双方的中介。

一帧页缓存包含一个或多个块缓存,数目取决于块大小。页缓存中的块与块之间没有间隙,对块的管理有单独的缓冲头结构。

   每个块缓存有一个缓冲头,它包含了与块缓存状态相关的所有管理数据,包括块号、块长度、访问计数器等等。缓冲头和块缓存数据在内存中分属完全独立的不同区域。

3.4       独立的块缓存

依然保存在页中,但无需关注块在页中的组织。

采用LRU管理

3.5       /proc/meminfoBuffersCached的区别

Buffers存储的是文件的元数据块

Cached存储的是文件的实际数据

4        数据回写

4.1       概念

数据总是在物理内存中操作,随后在随机的时间点写回到磁盘,以保存修改。

两种处理方式:周期性的和强制性的。

内存中的超级块结构中包含了脏块的链表,从这个链表中选择一部分或全部脏块写回近磁盘。

4.2       周期性写回

系统启动时默认启动2pdflush线程,并使其处于睡眠状态。在系统写操作并不繁忙时,定期启动pdflush线程,将一定量的脏页(默认是1024)写回磁盘,然后进入睡眠状态。

默认情况下,并发的pdflush线程是2,最大是8.。当系统中超过1秒钟没有空闲的pdflush线程,则启动一个新的pdflush。若某个线程的空闲时间超过1秒,则销毁它,但要保证系统中pdflush线程数不少于2个。

当前并发的pdflush线程数可通过/proc/sys/vm/nr_pdflush_threads查看,该参数只读不可改。

运作机制简述:

一个pdflush线程调用wb_kupdate函数来回写,回写的页总数不能超过1024个页,默认只有变脏一段时间的页才会被回写,还可以回写对应指定设备的脏页。由于回写需要上锁,因此具体过程是通过一个循环来实现,每次回写一部分,直至所有脏块写完或总数达到1024,写完后进入睡眠,过一定时间间隔再次被唤醒。

若某个页被内核其它部分锁定,则默认跳过它,下次pdflush启动时再去回写。

若一次回写的时间超过了wb_kupdate线程启动的时间间隔,则推迟1秒在调用wb_kupdate

回写时在遇到阻塞时,可选择等待或跳过,在struct writeback_control中设置sync_modeWB_SYNC_ALL时,称作数据完整性回写,在阻塞时等待,直至回写真正完成。

4.3       完全同步

将系统中所有脏页全部回写,用sync系统调用实现,有一个同名的用户程序。

4.4       强制写回

当需要更多的空闲内存时或来自用户的sync系统调用,内核启动强制回写函数wakeup_pdflush

某些强制写回并不等待回写完成,而是发出请求后即返回,除非是数据完整性同步。

若某个进程产生大量脏页,会强制这个进程调用回写函数balance_dirty_pagesmm/page-writeback.c)。

 

4.5       当今最新进展

pdflush在新版内核中已经被移除。

一个块设备上可能有几个脏块队列,每一个pdflush线程可以处理一个或多个队列,但每个块设备均有拥塞限制,默认是128个写请求。

原有的pdflush机制是和设备数无关的,写回队列和超级块挂钩,现在内核中回写机制是和设备挂钩的。每个块设备均有一个flush线程

 

5        proc文件系统

5.1       概述

proc文件系统是一种虚拟文件系统,其信息并没有存储在块设备上,而是在读取的时候,才从内存中动态生成内容。通过它,用户可以读取内核数据,并可以改变某些内核数据,从而改变系统的行为。

 

5.2       proc中文件的读写方法

proc中的文件的文件都是文本文件,可以用catless去读,用echo去写。需要注意的是,并不是每个文件都可写,有些是只读的。

 

6        通过proc文件系统管理缓存

/proc/sys/vm

dirty_background_bytes缓存中的脏数据达到设定值时,启动pdflush开始强制写回。默认值是0,即没有设定,其值实际上是计算得出,dirty_background_ratio * the amount of dirtyable memory

 

dirty_background_ratio缓存中的脏数据达到设定的比例时,启动pdflush开始强制写回。默认值是10。这个比例是指脏数据与可用内存之比,可用内存是指:未使用的内存+当前可回收的内存 (LRU中活动或非活动的内存页,包括可回收的缓存和映射的内存页)。如果dirty_background_bytes已设置,则此处的设置无效,其值根据dirty_background_bytes重新计算,dirty_background_bytes / the amount of dirtyable system memory。若其值大于dirty_ratio,则自动下降至dirty_ratio的一半。(在mm\page-writeback.c中的get_dirty_limits函数中计算)

 

dirty_ratio某个进程产生的脏页到达此比例时(默认为20%,但不能小于5%),开始强制写回。此时由于回写页数很多,导致设备上的写队列拥塞,因此这个设备上的IO操作会被阻塞,可能导致很多进程挂起,从而出现系统停止响应的现象。若系统写负荷较重,可适当降低dirty_background_ratiodirty_expire_centisecsdirty_writeback_centisecs等参数,使系统的回写在时间上尽量分散开。

 

dirty_bytes某个进程产生的脏页到达此字节数,开始强制写回。默认为0,即禁用,最小值不能少于两个内存页,否则使用旧设置的值。此参数优先级高于dirty_ratio,当被设置时,dirty_ratio根据它计算得出(dirty_bytes / the amount of dirtyable system memory)。设置非0数值后,dirty_ratio变为0。设置dirty_ratiodirty_bytes变为0.

 

dirty_expire_centisecs时间值,当页变脏时间超过此值时,pdflush将其写回。以百分之一秒为单位,默认值为3000,即30秒。

 

dirty_writeback_centisecs时间值,为pdflush的调用周期,以百分之一秒为单位,默认值为500,即5秒。

 

drop_caches用于释放页缓存、dentry缓存和inode缓存。值为0时代表禁用;值为1时释放页缓存;值为2时释放dentryinode缓存;值为3时释放页缓存、dentry缓存和inode缓存。注意:它并不能释放脏块。

 

nr_pdflush_threads当前并发的pdflush线程数,该参数只读不可改。

 

vfs_cache_pressure用于确定回收dentry缓存和inode缓存的策略,默认值是100。小于100表示倾向于保留dentry缓存和inode缓存,意味着尽管有一些缓存已经没有进程在使用它,但依然会保留下来,大于100则相反。如果为0则不回收它们,不过这样容易导致内存溢出,即没有可用内存分配给进程。

 

/sys/block/xxx/queue/nr_requests这个块设备读、写队列发生拥塞时的请求数上限,默认是128(读写都是128),即写请求数少于128时,认为这个设备还有能力接受更多的写请求,pdflush不会遇到阻塞。如果块设备性能好,可调大此参数。

 

 

7        示例

运行一个遍历目录的脚本,通过free命令可以看到buffer在上升,slabtop命令可以看到dentryext3_inode_cache都在上升,而cache不变。

停止脚本, echo 1 > /proc/sys/vm/drop_cachesbuffer迅速回落,几乎为0free命令中的使用的内存也减少同样的数量;dentryext3_inode_cache不变;

echo 2 > /proc/sys/vm/drop_cachesdentryext3_inode_cache迅速回落,free命令中的使用的内存也减少同样的数量。

结论:由于脚本只是遍历目录,没有写操作(除了向标准输出写目录名称),因此buffer中都是目录和inode信息,此信息也写进dentryext3_inode_cache,即有两份相同的拷贝。

 

8        The Swap Cache

When swapping pages out to the swap files, Linux avoids writing pages if it does not have to. There are times when a page is both in a swap file and in physical memory. This happens when a page that was swapped out of memory was then brought back into memory when it was again accessed by a process. So long as the page in memory is not written to, the copy in the swap file remains valid.

Linux uses the swap cache to track these pages. The swap cache is a list of page table entries, one per physical page in the system. This is a page table entry for a swapped out page and describes which swap file the page is being held in together with its location in the swap file. If a swap cache entry is non-zero, it represents a page which is being held in a swap file that has not been modified. If the page is subsequently modified (by being written to), its entry is removed from the swap cache.

When Linux needs to swap a physical page out to a swap file it consults the swap cache and, if there is a valid entry for this page, it does not need to write the page out to the swap file. This is because the page in memory has not been modified since it was last read from the swap file.

The entries in the swap cache are page table entries for swapped out pages. They are marked as invalid but contain information which allow Linux to find the right swap file and the right page within that swap file.


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多