如何得知某个文件是不是被setpagecachecapacity了

本文参与,欢迎正在阅读的你也加入,一起分享。|32 篇文章26 人订阅相关文章来自专栏12来自专栏114来自专栏16来自专栏12来自专栏168来自专栏25文件 Cache 管理是 Linux 内核中一个很重要并且较难理解的组成部分。本文详细介绍了 Linux 内核中文件 Cache 管理的各个方面,希望能够对开发者理解相关代码有所帮助。
自从诞生以来,Linux 就被不断完善和普及,目前它已经成为主流通用操作系统之一,使用得非常广泛,它与 Windows、UNIX 一起占据了操作系统领域几乎所有的市场份额。特别是在高性能计算领域,Linux 已经成为一个占主导地位的操作系统,在2005年6月全球TOP500 计算机中,有 301 台部署的是 Linux 操作系统。因此,研究和使用 Linux 已经成为开发者的不可回避的问题了。
下面我们介绍一下 Linux 内核中文件 Cache 管理的机制。本文以 2.6 系列内核为基准,主要讲述工作原理、数据结构和算法,不涉及具体代码。
2 操作系统和文件 Cache 管理
操作系统是计算机上最重要的系统软件,它负责管理各种物理资源,并向应用程序提供各种抽象接口以便其使用这些物理资源。从应用程序的角度看,操作系统提供了一个统一的虚拟机,在该虚拟机中没有各种机器的具体细节,只有进程、文件、地址空间以及进程间通信等逻辑概念。这种抽象虚拟机使得应用程序的开发变得相对容易:开发者只需与虚拟机中的各种逻辑对象交互,而不需要了解各种机器的具体细节。此外,这些抽象的逻辑对象使得操作系统能够很容易隔离并保护各个应用程序。
对于存储设备上的数据,操作系统向应用程序提供的逻辑概念就是"文件"。应用程序要存储或访问数据时,只需读或者写"文件"的一维地址空间即可,而这个地址空间与存储设备上存储块之间的对应关系则由操作系统维护。
在 Linux 操作系统中,当应用程序需要读取文件中的数据时,操作系统先分配一些内存,将数据从存储设备读入到这些内存中,然后再将数据分发给应用程序;当需要往文件中写数据时,操作系统先分配内存接收用户数据,然后再将数据从内存写到磁盘上。文件 Cache 管理指的就是对这些由操作系统分配,并用来存储文件数据的内存的管理。 Cache 管理的优劣通过两个指标衡量:一是 Cache 命中率,Cache 命中时数据可以直接从内存中获取,不再需要访问低速外设,因而可以显著提高性能;二是有效 Cache 的比率,有效 Cache 是指真正会被访问到的 Cache 项,如果有效 Cache 的比率偏低,则相当部分磁盘带宽会被浪费到读取无用 Cache 上,而且无用 Cache 会间接导致系统内存紧张,最后可能会严重影响性能。
下面分别介绍文件 Cache 管理在 Linux 操作系统中的地位和作用、Linux 中文件 Cache相关的数据结构、Linux 中文件 Cache 的预读和替换、Linux 中文件 Cache 相关 API 及其实现。
2 文件 Cache 的地位和作用
文件 Cache 是文件数据在内存中的副本,因此文件 Cache 管理与内存管理系统和文件系统都相关:一方面文件 Cache 作为物理内存的一部分,需要参与物理内存的分配回收过程,另一方面文件 Cache 中的数据来源于存储设备上的文件,需要通过文件系统与存储设备进行读写交互。从操作系统的角度考虑,文件 Cache 可以看做是内存管理系统与文件系统之间的联系纽带。因此,文件 Cache 管理是操作系统的一个重要组成部分,它的性能直接影响着文件系统和内存管理系统的性能。
图1描述了 Linux 操作系统中文件 Cache 管理与内存管理以及文件系统的关系示意图。从图中可以看到,在 Linux 中,具体文件系统,如 ext2/ext3、jfs、ntfs 等,负责在文件 Cache和存储设备之间交换数据,位于具体文件系统之上的虚拟文件系统VFS负责在应用程序和文件 Cache 之间通过 read/write 等接口交换数据,而内存管理系统负责文件 Cache 的分配和回收,同时虚拟内存管理系统(VMM)则允许应用程序和文件 Cache 之间通过 memory map的方式交换数据。可见,在 Linux 系统中,文件 Cache 是内存管理系统、文件系统以及应用程序之间的一个联系枢纽。
3 文件 Cache 相关数据结构
在 Linux 的实现中,文件 Cache 分为两个层面,一是 Page Cache,另一个 Buffer Cache,每一个 Page Cache 包含若干 Buffer Cache。内存管理系统和 VFS 只与 Page Cache 交互,内存管理系统负责维护每项 Page Cache 的分配和回收,同时在使用 memory map 方式访问时负责建立映射;VFS 负责 Page Cache 与用户空间的数据交换。而具体文件系统则一般只与 Buffer Cache 交互,它们负责在外围存储设备和 Buffer Cache 之间交换数据。Page Cache、Buffer Cache、文件以及磁盘之间的关系如图 2 所示,Page 结构和 buffer_head 数据结构的关系如图 3 所示。在上述两个图中,假定了 Page 的大小是 4K,磁盘块的大小是 1K。本文所讲述的,主要是指对 Page Cache 的管理。
在 Linux 内核中,文件的每个数据块最多只能对应一个 Page Cache 项,它通过两个数据结构来管理这些 Cache 项,一个是 radix tree,另一个是双向链表。Radix tree 是一种搜索树,Linux 内核利用这个数据结构来通过文件内偏移快速定位 Cache 项,图 4 是 radix tree的一个示意图,该 radix tree 的分叉为4(22),树高为4,用来快速定位8位文件内偏移。Linux(2.6.7) 内核中的分叉为 64(26),树高为 6(64位系统)或者 11(32位系统),用来快速定位 32 位或者 64 位偏移,radix tree 中的每一个叶子节点指向文件内相应偏移所对应的Cache项。
另一个数据结构是双向链表,Linux内核为每一片物理内存区域(zone)维护active_list和inactive_list两个双向链表,这两个list主要用来实现物理内存的回收。这两个链表上除了文件Cache之外,还包括其它匿名(Anonymous)内存,如进程堆栈等。
4 文件Cache的预读和替换
Linux内核中文件预读算法的具体过程是这样的:对于每个文件的第一个读请求,系统读入所请求的页面并读入紧随其后的少数几个页面(不少于一个页面,通常是三个页面),这时的预读称为同步预读。对于第二次读请求,如果所读页面不在Cache中,即不在前次预读的group中,则表明文件访问不是顺序访问,系统继续采用同步预读;如果所读页面在Cache中,则表明前次预读命中,操作系统把预读group扩大一倍,并让底层文件系统读入group中剩下尚不在Cache中的文件数据块,这时的预读称为异步预读。无论第二次读请求是否命中,系统都要更新当前预读group的大小。此外,系统中定义了一个window,它包括前一次预读的group和本次预读的group。任何接下来的读请求都会处于两种情况之一:第一种情况是所请求的页面处于预读window中,这时继续进行异步预读并更新相应的window和group;第二种情况是所请求的页面处于预读window之外,这时系统就要进行同步预读并重置相应的window和group。图5是Linux内核预读机制的一个示意图,其中a是某次读操作之前的情况,b是读操作所请求页面不在window中的情况,而c是读操作所请求页面在window中的情况。
Linux内核中文件Cache替换的具体过程是这样的:刚刚分配的Cache项链入到inactive_list头部,并将其状态设置为active,当内存不够需要回收Cache时,系统首先从尾部开始反向扫描active_list并将状态不是referenced的项链入到inactive_list的头部,然后系统反向扫描inactive_list,如果所扫描的项的处于合适的状态就回收该项,直到回收了足够数目的Cache项。Cache替换算法如图6的算法描述伪码所示。
图6 Linux的Cache替换算法描述
Mark_Accessed(b) {
if b.state==(UNACTIVE && UNREFERENCE)
b.state = REFERENCE
else if b.state == (UNACTIVE && REFERENCE) {
b.state = (ACTIVE && UNREFERENCE)
Add X to tail of active_list
} else if b.state == (ACTIVE && UNREFERENCE)
b.state = (ACTIVE && REFERENCE)
Reclaim() {
if active_list not empty and scan_num&MAX_SCAN1
X = head of active_list
if (X.state & REFERENCE) == 0
Add X to tail of inactive_list
X.state &=
~REFERENCE
Move X to tail of active_list
scan_num++
scan_num = 0
if inactive_list not emptry and scan_num &
MAX_SCAN2 {
X = head of inactive_list
if (X.state & REFERENCE) == 0
X.state = ACTIVE | UNREFERENCE
Move X to tail of active_list
scan_num++
return NULL
Access(b){
if b is not in cache {
if slot X free
put b into X
X=Reclaim()
put b into X
Add X to tail of inactive_list
Mark_Accessed(X)
5 文件Cache相关API及其实现
Linux内核中与文件Cache操作相关的API有很多,按其使用方式可以分成两类:一类是以拷贝方式操作的相关接口, 如read/write/sendfile等,其中sendfile在2.6系列的内核中已经不再支持;另一类是以地址映射方式操作的相关接口,如mmap等。
第一种类型的API在不同文件的Cache之间或者Cache与应用程序所提供的用户空间buffer之间拷贝数据,其实现原理如图7所示。
第二种类型的API将Cache项映射到用户空间,使得应用程序可以像使用内存指针一样访问文件,Memory map访问Cache的方式在内核中是采用请求页面机制实现的,其工作过程如图8所示。
首先,应用程序调用mmap(图中1),陷入到内核中后调用do_mmap_pgoff(图中2)。该函数从应用程序的地址空间中分配一段区域作为映射的内存地址,并使用一个VMA(vm_area_struct)结构代表该区域,之后就返回到应用程序(图中3)。当应用程序访问mmap所返回的地址指针时(图中4),由于虚实映射尚未建立,会触发缺页中断(图中5)。之后系统会调用缺页中断处理函数(图中6),在缺页中断处理函数中,内核通过相应区域的VMA结构判断出该区域属于文件映射,于是调用具体文件系统的接口读入相应的Page Cache项(图中7、8、9),并填写相应的虚实映射表。经过这些步骤之后,应用程序就可以正常访问相应的内存区域了。
文件Cache管理是Linux操作系统的一个重要组成部分,同时也是研究领域一个很热门的研究方向。目前,Linux内核在这个方面的工作集中在开发更有效的Cache替换算法上,如LIRS(其变种ClockPro)、ARC等。相关信息可见。
阅读(...) 评论()&nbsp>&nbsp
&nbsp>&nbsp
&nbsp>&nbsp
Linux文件系统(四)---三大缓冲区之inode缓冲区 (内存inode映像 )
摘要:在文件系统中,有三大缓冲为了提升效率:inode缓冲区、dentry缓冲区、块缓冲。(内核:2.4.37)一、inode缓冲区为了加快对索引节点的索引,引入inode缓冲区,下面我们看Linux/fs/inode.c代码。inode缓冲区代码1、一些数据结构:之前已经说过,有多个链表用于管理inode节点:[cpp]&viewplaincopyprint?&span&style=&font-size:14&&59&nb
在文件系统中,有三大缓冲为了提升效率:inode缓冲区、dentry缓冲区、块缓冲。
(内核:2.4.37)
一、inode缓冲区
为了加快对索引节点的索引,引入inode缓冲区,下面我们看Linux/fs/inode.c代码。inode缓冲区代码
1、一些数据结构:
之前已经说过,有多个链表用于管理inode节点:
[cpp]&view plaincopyprint?
&span&style=&font-size:14&&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_&&
&63&static&LIST_HEAD(anon_hash_chain);&/*&for&inodes&with&NULL&i_sb&*/&&
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。[cpp]&view plaincopyprint?
&span&style=&font-size:14&&&76&struct&inodes_stat_t&inodes_&&
&78&static&kmem_cache_t&*&inode_&&
上面的两个字段:inodes_stat:记录inodes节点的状态。inode_cachep:对inodes对象的缓存块。2、基本初始化:初始化inode哈希表头和slab内存缓存块索引节点高速缓存的初始化是由inode_init()实现的,现在看看下面代码:[cpp]&view plaincopyprint?
&span&style=&font-size:14&&1296&/*&
1297&&*&Initialize&the&hash&tables.&
1298&&*/&&
1299&void&__init&inode_init(unsigned&long&mempages)&&&/*&参数:表示inode缓存使用的物理页面数&*/&&
1301&&&&&&&&&struct&list_head&*&&
1302&&&&&&&&&unsigned&long&&&
1303&&&&&&&&&unsigned&int&nr_&&
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)&&&&order++)&&
1309&&&&&&&&&&&&&&&&&;&&
1311&&&&&&&&&do&{&&
1312&&&&&&&&&&&&&&&&&unsigned&long&&&
1314&&&&&&&&&&&&&&&&&nr_hash&=&(1UL&&&&order)&*&PAGE_SIZE&/&&
1315&&&&&&&&&&&&&&&&&&&&&&&&&sizeof(struct&list_head);&&
1316&&&&&&&&&&&&&&&&&i_hash_mask&=&(nr_hash&-&1);&&
1318&&&&&&&&&&&&&&&&&tmp&=&nr_&&
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);&&
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_&&
1334&&&&&&&&&i&=&nr_&&
1335&&&&&&&&&do&{&&
1336&&&&&&&&&&&&&&&&&INIT_LIST_HEAD(head);&&
1337&&&&&&&&&&&&&&&&&head++;&&
1338&&&&&&&&&&&&&&&&&i--;&&
1339&&&&&&&&&}&while&(i);&&
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&);&&
1348&&&&&&&&&unused_inodes_flush_task.routine&=&try_to_sync_unused_&&
注意上面的逻辑,说明两个问题:1). &第一初始化inode_hashtable作为链表的头。2). &初始化inode的slab缓存,也就是说,如果我需要分配一个inode缓存在内存中,那么都从这个inode_cachep中分配一个inode内存节点。然后统一加入到这个inode_hashtable中进行管理!也就是所谓的创建inode slab分配器缓存。下面看看具体的缓存的分配过程:先看init_once函数:[cpp]&view plaincopyprint?
&span&style=&font-size:14&&169&static&void&init_once(void&*&foo,&kmem_cache_t&*&cachep,&unsigned&long&flags)&&
171&&&&&&&&&struct&inode&*&inode&=&(struct&inode&*)&&&
173&&&&&&&&&if&((flags&&;&(SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR))&==&&
174&&&&&&&&&&&&&SLAB_CTOR_CONSTRUCTOR)&&
175&&&&&&&&&&&&&&&&&inode_init_once(inode);&&
注意:在上面的kmem_cache_create函数中,执行的顺序是:---& kmem_cache_create(里面重要的一步是cachep-&ctor = cachep-&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函数:[cpp]&view plaincopyprint?
&span&style=&font-size:14&&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.&
146&void&inode_init_once(struct&inode&*inode)&&
148&&&&&&&&&memset(inode,&0,&sizeof(*inode));&&
149&&&&&&&&&__inode_init_once(inode);&&
再看__inode_init_once函数:[cpp]&view plaincopyprint?
&span&style=&font-size:14&&152&void&__inode_init_once(struct&inode&*inode)&&
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);&&
3、注意知道现在我们主要说了上面的两个基本的问题(红字部分),但是这只是一个框架而已,对于具体的一个文件系统来说怎么个流程,下面需要看看!我们以最常见的ext2作为说明:现在一个ext2类型的文件系统想要创建一个inode,那么执行:ext2_new_inode函数[cpp]&view plaincopyprint?
&span&style=&font-size:14&&314&struct&inode&*&ext2_new_inode&(const&struct&inode&*&dir,&int&mode)&&
316&&&&&&&&&struct&super_block&*&&&
317&&&&&&&&&struct&buffer_head&*&&&
318&&&&&&&&&struct&buffer_head&*&bh2;&&
319&&&&&&&&&int&group,&i;&&
320&&&&&&&&&ino_t&&&
321&&&&&&&&&struct&inode&*&&&
322&&&&&&&&&struct&ext2_group_desc&*&&&
323&&&&&&&&&struct&ext2_super_block&*&&&
324&&&&&&&&&int&&&
326&&&&&&&&&sb&=&dir-&i_&&
327&&&&&&&&&inode&=&new_inode(sb);&&&/*&创建一个inode节点,这个函数就是在fs/inode.c中的new_inode函数&*/&&
328&&&&&&&&&if&(!inode)&&
329&&&&&&&&&&&&&&&&&return&ERR_PTR(-ENOMEM);&&
331&&&&&&&&&lock_super&(sb);&&
332&&&&&&&&&es&=&sb-&u.ext2_sb.s_&&
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);&&
339&&&&&&&&&err&=&-ENOSPC;&&
340&&&&&&&&&if&(group&==&-1)&&
341&&&&&&&&&&&&&&&&&goto&&&
343&&&&&&&&&err&=&-EIO;&&
344&&&&&&&&&bh&=&load_inode_bitmap&(sb,&group);&&
345&&&&&&&&&if&(IS_ERR(bh))&&
346&&&&&&&&&&&&&&&&&goto&fail2;&&
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_&&
352&&&&&&&&&ext2_set_bit&(i,&bh-&b_data);&&
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&&&&&&&&&}&&
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&&&&&&&&&}&&
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-&&&
374&&&&&&&&&if&(test_opt&(sb,&GRPID))&&
375&&&&&&&&&&&&&&&&&inode-&i_gid&=&dir-&i_&&
376&&&&&&&&&else&if&(dir-&i_mode&&;&S_ISGID)&{&&
377&&&&&&&&&&&&&&&&&inode-&i_gid&=&dir-&i_&&
378&&&&&&&&&&&&&&&&&if&(S_ISDIR(mode))&&
379&&&&&&&&&&&&&&&&&&&&&&&&&mode&|=&S_ISGID;&&
380&&&&&&&&&}&else&&
381&&&&&&&&&&&&&&&&&inode-&i_gid&=&current-&&&
382&&&&&&&&&inode-&i_mode&=&&&
384&&&&&&&&&inode-&i_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&=&&&
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);&&
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&&&
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);&&
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&&&
432&&&&&&&&&desc&=&ext2_get_group_desc&(sb,&group,&&;bh2);&&
433&&&&&&&&&desc-&bg_free_inodes_count&=&0;&&
434&&&&&&&&&mark_buffer_dirty(bh2);&&
435&&&&&&&&&goto&&&
这个函数比较复杂,但是我们主要看327行和394行,就是创建一个inode内存节点,然后将这个inode插入inode_hashtable中!这个函数具体的解释不再看了,现在主要从这两个函数入手:1). fs/inode.c中的new_inode函数,创建一个inode内存节点:[cpp]&view plaincopyprint?
&span&style=&font-size:14&&964&struct&inode&*&new_inode(struct&super_block&*sb)&&
966&&&&&&&&&static&unsigned&long&last_&&
967&&&&&&&&&struct&inode&*&&&
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_&/*&给这个inode分配一个inode号!&*/&&
977&&&&&&&&&&&&&&&&&inode-&i_state&=&0;&&
978&&&&&&&&&&&&&&&&&spin_unlock(&;inode_lock);&&
979&&&&&&&&&}&&
980&&&&&&&&&return&&&
看看这个alloc_inode函数:[cpp]&view plaincopyprint?
&span&style=&font-size:14&&&80&static&struct&inode&*alloc_inode(struct&super_block&*sb)&&
&82&&&&&&&&&static&struct&address_space_operations&empty_&&
&83&&&&&&&&&static&struct&inode_operations&empty_&&
&84&&&&&&&&&static&struct&file_operations&empty_&&
&85&&&&&&&&&struct&inode&*&&
&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_&&
&99&&&&&&&&&&&&&&&&&inode-&i_sb&=&&&
100&&&&&&&&&&&&&&&&&inode-&i_dev&=&sb-&s_&&
101&&&&&&&&&&&&&&&&&inode-&i_blkbits&=&sb-&s_blocksize_&&
102&&&&&&&&&&&&&&&&&inode-&i_flags&=&0;&&
103&&&&&&&&&&&&&&&&&atomic_set(&;inode-&i_count,&1);&&
104&&&&&&&&&&&&&&&&&inode-&i_sock&=&0;&&
105&&&&&&&&&&&&&&&&&inode-&i_op&=&&;empty_&&
106&&&&&&&&&&&&&&&&&inode-&i_fop&=&&;empty_&&
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;&&
118&&&&&&&&&&&&&&&&&mapping-&a_ops&=&&;empty_&&
119&&&&&&&&&&&&&&&&&mapping-&host&=&&&
120&&&&&&&&&&&&&&&&&mapping-&gfp_mask&=&GFP_HIGHUSER;&&
121&&&&&&&&&&&&&&&&&inode-&i_mapping&=&&&
122&&&&&&&&&}&&
123&&&&&&&&&return&&&
我们主要看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中:[cpp]&view plaincopyprint?
&span&style=&font-size:14&&1166&void&insert_inode_hash(struct&inode&*inode)&&
1168&&&&&&&&&struct&list_head&*head&=&&;anon_hash_&&/*&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);&&
注意这个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中):[cpp]&view plaincopyprint?
&span&style=&font-size:14&&1043&static&inline&unsigned&long&hash(struct&super_block&*sb,&unsigned&long&i_ino)&&
1045&&&&&&&&&unsigned&long&tmp&=&i_ino&+&((unsigned&long)&sb&/&L1_CACHE_BYTES);&&
1046&&&&&&&&&tmp&=&tmp&+&(tmp&&&&I_HASHBITS);&&
1047&&&&&&&&&return&tmp&&;&I_HASHMASK;&&
OK,上面的具体的inode创建和加入的流程基本清楚了。具体创建的过程是涉及到内存这一块的,不多说了。4. 下面看看给一个怎么去找到一个inode,涉及ilookup函数:[cpp]&view plaincopyprint?
&span&style=&font-size:14&&1102&struct&inode&*ilookup(struct&super_block&*sb,&unsigned&long&ino)&&
1104&&&&&&&&&struct&list_head&*&head&=&inode_hashtable&+&hash(sb,ino);/*&获得hash值&*/&&
1105&&&&&&&&&struct&inode&*&&&
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&&&
1114&&&&&&&&&}&&
1115&&&&&&&&&spin_unlock(&;inode_lock);&&
1117&&&&&&&&&return&&&
这个函数其实比较简单了,首先还是获得这个inode的hash值定位,然后开始finde_inode:[cpp]&view plaincopyprint?
&span&style=&font-size:14&&929&static&struct&inode&*&find_inode(struct&super_block&*&sb,&unsigned&long&ino,&struct&list_head&*head,&find_inode_t&find_actor,&void&*opaque)&&
931&&&&&&&&&struct&list_head&*&&
932&&&&&&&&&struct&inode&*&&&
934&repeat:&&
935&&&&&&&&&tmp&=&&&
936&&&&&&&&&for&(;;)&{&&
937&&&&&&&&&&&&&&&&&tmp&=&tmp-&&&
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&&&
951&&&&&&&&&&&&&&&&&}&&
952&&&&&&&&&&&&&&&&&break;&&
953&&&&&&&&&}&&
954&&&&&&&&&return&&/*返回找到的inode节点*/&&
上面函数最核心的本质不就是双向链表的查找么,OK。最后:关于inode怎么工作的,将会在后面的分析ext2代码中在详细研究。
以上是的内容,更多
的内容,请您使用右上方搜索功能获取相关信息。
若你要投稿、删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内给你回复。
云服务器 ECS
可弹性伸缩、安全稳定、简单易用
&40.8元/月起
预测未发生的攻击
&24元/月起
邮箱低至5折
推荐购买再奖现金,最高25%
&200元/3月起
你可能还喜欢
你可能感兴趣
阿里云教程中心为您免费提供
Linux文件系统(四)---三大缓冲区之inode缓冲区 (内存inode映像 )相关信息,包括
的信息,所有Linux文件系统(四)---三大缓冲区之inode缓冲区 (内存inode映像 )相关内容均不代表阿里云的意见!投稿删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内答复
售前咨询热线
支持与服务
资源和社区
关注阿里云
International

我要回帖

更多关于 django cache page 的文章

 

随机推荐