问题背景
一个内核模块中,需要通过d_path接口获取文件的路径,然后与目标文件白名单做匹配。
在生产环境中,获取的文件是存在的,但是与文件白名单中的文件总是匹配失败。
问题定位:
通过打印d_path返回的字符串,发现获得的路径后面多了一个" (deleted)"字符串,在做完全匹配时不通过。
看了d_path函数说明:如果entry被删除了,会添加" (deleted)"字符串。
* Convert a dentry into an ASCII path name. If the entry has been deleted
* the string " (deleted)" is appended. Note that this is ambiguous.
为什么会这样,还要从Linux文件系统的dcache、inode和dentry说起。
linux文件系统
Linux能够成功的其中一个关键是它能够很好地与其他系统共存。你可以透明地挂载由Windows或其他Unix系统格式化
的磁盘或者分区。Linux与其他Unix系统一样,可以通过虚拟文件系统管理支持多种文件系统类型(Virtual Filesystem)。
内核中虚拟文件系提供了能够表示许多不同类型文件系统的广泛信息;Linux有字段或者函数能够支持所有真实文件系统
的每一个操作。对于每个read、write或其他调用的函数,内核替代真正的函数来支持本地Linux文件系统,NTFS文件系统,
或者其他任何的文件系统。
有一张图,可以表示进程与VFS各种对象之间的关系,来源于《深入理解Linux内核》,如下所示:
superblock对象:
存储被挂载的文件系统的相关信息。对于磁盘文件系统,它就是文件系统控制块(filesystem control block)。
inode对象
存储某一特定文件的一般信息。对于磁盘文件系统,它就是文件控制块(file control block)。每个inode对象
都有一个inode数字,它唯一确定了文件系统内的文件。
file对象
存储进程与其打开的文件之间的互操作信息。这些信息仅存在于内核内存中,在进程打开文件期间有效。
dentry对象
存储相关文件的目录条目(directory entry,也就是特殊的文件)的链接信息。每个磁盘文件系统都有自己的
特别方式存储这些信息。
从上图可以看到,在文件对象和dentry对象之间,还有一个dentry cache(简称dcache),它是文件系统性能
的一个关键角色。所有最近使用的dentry对象都包含在磁盘cache中,它叫做dentry cache。它能加快从文件路径到
最后一个路径组件的转换。
通常来说,磁盘cache是软件机制,它允许内核在RAM中保存一些存储在磁盘上的信息。因此,它可以很好地
满足对这些数据的进一步快速访问,而不需要通过磁盘本身做慢速访问。
Page Frame回收机制
上面也提到,dentry cache是软件机制,保存最近访问文件的dentry对象,那它就不会是无限扩大,系统总是会
根据实际情况做dentry的回收和老化。
在Linux系统中,由PFRA(Page Frame Reclaiming Algorithm)决定dentry cache回收。当它尝试重新申请Page
Frame,它也要检查dentry cache是否需要收缩。
Linux系统PFRA从disk cache回收page的注册函数是shrink_slab,由try_to_free_pages-->shrink_slab调用。
总结:
由上面可以得出,当系统通过PFRA释放了dentry条目,就会把dentry从disk cache链表中删除,但是其值还在,
所以在d_path中,会得到带" (deleted)"的字符串。
教训&经验
内核代码接口很强大,学艺不精,导致问题
使用内核接口之前,要仔细研读函数说明,了解细节和应用场景
参考自己的应该场景,做适当的处理。
参考资料:
1.《深入理解Linux内核》(第三版)