📄 036_fs_dcache_c.html
字号:
(NFS</FONT><BR>
<FONT color=#3333ff> * timeouts or autofs deletes).</FONT><BR>
<FONT color=#3333ff> */</FONT><BR>
static __inline__ void <FONT color=#006600><B>d_drop</B></FONT>(struct
dentry * dentry)<BR>
{<BR>
spin_lock(&dcache_lock);<BR>
list_del(&dentry->d_hash);<BR>
INIT_LIST_HEAD(&dentry->d_hash);<BR>
spin_unlock(&dcache_lock);<BR>
}<BR>
<BR>
然后删除一个文件时,对detnry的影响是d_delete, 注意delete总是试图只reset inode,
使其变为negetive的dentry,除非还有其他object还在引用这个文件:<BR>
<FONT color=#3333ff>/*</FONT><BR>
<FONT color=#3333ff> * When a file is deleted, we have two
options:</FONT><BR>
<FONT color=#3333ff> * - <B>turn this dentry into a negative</B>
dentry</FONT><BR>
<FONT color=#3333ff> * - <B>unhash this dentry and free
it.</B></FONT><BR>
<FONT color=#3333ff> *</FONT><BR>
<FONT color=#3333ff> * Usually, we want to just turn this
into</FONT><BR>
<FONT color=#3333ff> * a negative dentry, but if anybody else
is</FONT><BR>
<FONT color=#3333ff> * currently using the dentry or the
inode</FONT><BR>
<FONT color=#3333ff> * we can't do that and we fall back on
removing</FONT><BR>
<FONT color=#3333ff> * it from the hash queues and waiting
for</FONT><BR>
<FONT color=#3333ff> * it to be deleted later when it has no
users</FONT><BR>
<FONT color=#3333ff> */</FONT><BR>
void <FONT color=#006600><B>d_delete</B></FONT>(struct dentry * dentry)<BR>
{<BR>
/*<BR>
* Are we the only user?<BR>
*/<BR>
spin_lock(&dcache_lock);<BR>
if (atomic_read(&dentry->d_count) == 1) {<BR>
dentry_iput(dentry);<BR>
return;<BR>
}<BR>
spin_unlock(&dcache_lock);<BR>
<BR>
/*<BR>
* If not, just drop the dentry and let dput<BR>
* pick up the tab..<BR>
*/<BR>
d_drop(dentry);<BR>
}<BR>
为了清楚到底如何分配和释放一个dentry,还是简单看看文件删除,目录删除,和shrink dcache 的主要流程吧:<BR>
sys_unlink ->vfs_unlink <B>-> </B>dir->i_op->unlink<B> +
d_delete +dput</B><BR>
sys_rmdir->vfs_rmdir -> dir->i_op->rmdir + <B>d_unhash
</B>(<FONT color=#009900><B>d_drop</B></FONT>+shrink_dcache_parent) +<B>
d_delete+</B><FONT color=#000000><B>dput</B></FONT><BR>
kswapd || bdflush -> ...
-->shrink_dcache_memory->prune_dcache->(d_drop +inode put+
d_free)<BR>
首先第一个问题是:<BR>
为啥删除的文件的dentry留在目录树中?
呵呵,快呗,起码我从内存就知道,这个文件已经被删除.(不过首先被prune,unlink的时候会被转移到unused 链表,呵呵).
曾经在哪里见过说unused队列中的dentry中还保留有inode.....起码是不太对头的. (大家讨论).<BR>
<BR>
在分析dput前,看看一个lock free的原子操作,有助理解dcache的实现:<FONT color=#cc0000 size=4>(lock
free 的算法)</FONT><BR>
int <FONT color=#006600><B>atomic_dec_and_lock</B></FONT>(atomic_t
*atomic, spinlock_t *lock)<BR>
{<BR>
int counter;<BR>
int newcount;<BR>
<BR>
repeat:<BR>
counter = atomic_read(atomic);<BR>
newcount = counter-1;<BR>
<BR>
if (!newcount)
<FONT color=#3333ff>/*如果是最后一个引用则需要加锁*/</FONT><BR>
goto slow_path;<BR>
<FONT color=#3333ff>/*不是最后一个引用采用的是lock free
的算法!!!!*/</FONT><BR>
asm volatile("lock; cmpxchgl %1,%2"<BR>
:"=a" (newcount)<BR>
:"r" (newcount), "m"
(atomic->counter), "0" (counter));<BR>
<BR>
/* If the above failed, "eax" will have changed */<BR>
if (newcount != counter)<BR>
goto repeat;<BR>
return 0;
<FONT color=#3333ff>/*不是最后一个引用返回0*/</FONT><BR>
<BR>
slow_path:<BR>
spin_lock(lock);<BR>
if (atomic_dec_and_test(atomic))<BR>
return 1;<FONT color=#000099>
/*最后一个引用返回1*/</FONT><BR>
spin_unlock(lock);<BR>
return 0;<BR>
}<BR>
<BR>
<BR>
/*<BR>
* This is dput<BR>
*<BR>
* This is complicated by the fact that we do not want to put<BR>
* dentries that are no longer on any hash chain on the unused<BR>
* list: we'd much rather just get rid of them immediately.<BR>
*<BR>
* However, that implies that we have to traverse the dentry<BR>
* tree upwards to the parents which might _also_ now be<BR>
* scheduled for deletion (it may have been only waiting for<BR>
* its last child to go away).<BR>
*<BR>
* This tail recursion is done by hand as we don't want to depend<BR>
* on the compiler to always get this right (gcc generally doesn't).<BR>
* Real recursion would eat up our stack space.<BR>
*/<BR>
<BR>
/*<BR>
* dput - release a dentry<BR>
* @dentry: dentry to release<BR>
*<BR>
* Release a dentry. This will drop the usage count and if
appropriate<BR>
* call the dentry unlink method as well as removing it from the
queues and<BR>
* releasing its resources. If the parent dentries were scheduled for
release<BR>
* they too may now get deleted.<BR>
*<BR>
* no dcache lock, please.<BR>
*/<BR>
<BR>
void dput(struct dentry *dentry)<BR>
{<BR>
if (!dentry)<BR>
return;<BR>
<BR>
repeat:<BR>
if (!atomic_dec_and_lock(&dentry->d_count,
&dcache_lock))<BR>
return;<FONT color=#3333ff>
/*不是最后一个引用则无事可做, 看来到unused list的dentry连引用计数都是0*/</FONT><BR>
<BR>
/* dput on a free dentry? */<BR>
if (!list_empty(&dentry->d_lru)) /*bug check*/<BR>
BUG();<BR>
/*<BR>
* AV: ->d_delete() is _NOT_ allowed to block
now.<BR>
*/<BR>
if (dentry->d_op &&
dentry->d_op->d_delete) { <FONT color=#3333ff>/*自己实现d
delete的文件系统不多,ext2就没有的*/</FONT><BR>
if
(dentry->d_op->d_delete(dentry))<BR>
goto unhash_it;<BR>
}<BR>
/* Unreachable? Get rid of it */<BR>
if (list_empty(&dentry->d_hash))
<FONT color=#3333ff>/*已经unhash?
,delete的时候还用别的obj或者过程在用,不过现在没有了*/</FONT><BR>
goto kill_it;<BR>
list_add(&dentry->d_lru,
&dentry_unused);<FONT color=#3333ff> /*普通的删除就放到unused队列中*/</FONT><BR>
dentry_stat.nr_unused++;<BR>
/*<BR>
* Update the timestamp<BR>
*/<BR>
dentry->d_reftime = jiffies;<BR>
spin_unlock(&dcache_lock);<BR>
return;<BR>
<BR>
unhash_it:<BR>
list_del_init(&dentry->d_hash);<BR>
<BR>
kill_it: {<BR>
struct dentry *parent;<BR>
list_del(&dentry->d_child);<BR>
/* drops the lock, at that point
nobody can reach this dentry */<BR>
dentry_iput(dentry);<BR>
<FONT color=#3333ff> parent =
dentry->d_parent</FONT>; <FONT color=#3333ff>/*对parent
dentry尝试进行dput操作*/</FONT><BR>
d_free(dentry);<BR>
if (dentry == parent)<BR>
return;<BR>
dentry = parent;<BR>
goto repeat;<BR>
}<BR>
}<BR>
<BR>
为了理解dput为啥需要"递归"到父节点进行操作,又为啥看到unhash的删除方式(而不是make negative),我们考虑这样一个操作流程:<BR>
假设有一个目录: home/usrx, 内有文件 home/usrx/myfile<BR>
操作流程为(fix me,这样容许不?):<BR>
0) open myfile<BR>
1) rm -r home/usrx<BR>
2) close myfile<BR>
<BR>
当rm home/usrx的时候由于myfile有步骤0)的引用,所以 d_delete不能negative这个dentry, 只是unhash他,
其父节点home/usrx亦同.<BR>
然后当close myfile的时候进行 dput,就有必要"递归"到父节点了.
(在这种情况下直接free掉myfile的dentry也是有道理的,父节点都没有了,呵呵,不用留着快速查找deleted的文件了).<BR>
<BR>
static inline void dentry_iput(struct dentry * dentry) 就不多说了.<BR>
<BR>
<DIV style=TEXT-ALIGN:center>
<FONT size=4><B>shrink and prune</B></FONT><BR>
</DIV>
<BR>
void shrink_dcache_memory(int priority, unsigned int gfp_mask) 就不多费口舌了.<BR>
/**<BR>
* prune_dcache - shrink the dcache<BR>
* @count: number of entries to try and free<BR>
*<BR>
* Shrink the dcache. This is done when we need<BR>
* more memory, or simply when we need to unmount<BR>
* something (at which point we need to unuse<BR>
* all dentries).<BR>
*<BR>
* This function may fail to free any resources if<BR>
* all the dentries are in use.<BR>
*/<BR>
<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -