📄 038_fs_dquot_c.html
字号:
flow: static(header); } /* used to insert page numbers */ div.google_header::before, div.google_footer::before { position: absolute; top: 0; } div.google_footer { flow: static(footer); } /* always consider this element at the start of the doc */ div#google_footer { flow: static(footer, start); } span.google_pagenumber { content: counter(page); } span.google_pagecount { content: counter(pages); } } @page { @top { content: flow(header); } @bottom { content: flow(footer); } } /* end default print css */ /* custom css *//* end custom css */ /* ui edited css */ body { font-family: Verdana; font-size: 10.0pt; line-height: normal; background-color: #ffffff; } .documentBG { background-color: #ffffff; } /* end ui edited css */</style> </head> <body revision="dcbsxfpf_23czwkt8:158"> <table align=center cellpadding=0 cellspacing=0 height=5716 width=800>
<tbody>
<tr>
<td height=5716 valign=top width=802>
<pre>2007-11-29<br><br> 照理说这个模块的耦合做的真是不错的. 偌大一堆函数是static, 非static的函数也就两三个是真正的接口函数而已. 在2.4中只有<br>ext2文件系统实现了disk quota,其他的文件系统还不支持. disk quota是照着BSD接口来实现的(见注释).<br> 顺着文件看下去,<br><font size=5><b>1. 首先知道, quot分成两种,一种针对user,一种针对group<br>也就是说一种针对uid,一种针对gid. </b></font><br>#define <font color=#000099>MAXQUOTAS</font> 2<br>#define <font color=#000099>USRQUOTA</font> 0 /* element used for user quotas */<br>#define <font color=#000099>GRPQUOTA </font> 1 /* element used for group quotas */<br><font size=4><br><font size=5><b>2. quot的使能的单位是文件系统,一个super block, sb</b></font></font><br>struct <font color=#000099><b>super_block</b></font> {<br> struct file_system_type *s_type;<br> struct <font color=#009900>super_operations</font> *<font color=#000099>s_op</font>;<br> struct <font color=#006600>dquot_operations</font> *<font color=#000099><b>dq_op</b></font>; <font color=#3333ff>/*除了s_op,还有disk quot 的ops, dq 就是disk quot*/</font><br><br> struct <font color=#009900>quota_mount_option</font>s <font color=#000099><b>s_dquo</b></font>t; <font color=#3333ff>/* Diskquota specific options */ quot的全局配置参数也存在sb中<br> /*这些配置里边我们先关心那个</font>files:这个文件是存储uid/gid对应的quot值的<font color=#3333ff>:分别存在两个文件中,以gid/uid为索引*/<br></font>...........<br>}<br>option:<br>struct <font color=#000099><b>quota_mount_options</b></font><br>{<br> unsigned int flags; /* Flags for diskquotas on this device */<br> struct semaphore dqio_sem; /* lock device while I/O in progress */<br> struct semaphore dqoff_sem; /* serialize quota_off() and quota_on() on device */<br> struct file *files[MAXQUOTAS]; <font color=#3333ff>/* fp's to quotafiles */ /*quot文件存储uid/gid对应的各种limit*/</font><br> time_t inode_expire[MAXQUOTAS]; <font color=#3333ff>/* expiretime for inode-quota */</font><br> time_t block_expire[MAXQUOTAS]; <font color=#3333ff>/* expiretime for block-quota */</font><br> char rsquash[MAXQUOTAS]; <font color=#3333ff>/* for quotas threat root as any other user */</font><br>};<br><br>/*<br> * Definitions for disk quotas imposed on the average user<br> * (big brother finally hits Linux).<br> *<br> * The following constants define the amount of time given a user<br> * before the soft limits are treated as hard limits (usually resulting<br> * in an allocation failure). The timer is started when the user crosses<br> * their soft limit, it is reset when they go below their soft limit.<br> */<br>#define <font color=#000099><b>MAX_IQ_TIME</b></font> 604800 /* (7*24*60*60) 1 week */<br>#define <font color=#000099>MAX_DQ_TIME</font> 604800 /* (7*24*60*60) 1 week */<br>为了理解hard quot和soft quot以expiretime, 只需看看<font color=#000099><b>check_idq</b></font>或者<font color=#000099><b>check_bdq</b></font>即可.<br><br><font size=5><b>3. 在quot文件中存储的数据如下(quot文件本身没有quot的限制)</b></font><br>/*<br> * The following structure defines the format of the disk quota file<br> * (as it appears on disk) - the file is an array of these structures<br> * indexed by user or group number.<br> */<br>struct <font color=#000099><b>dqblk</b></font> {<br> __u32 dqb_bhardlimit; /* absolute limit on disk blks alloc */<br> __u32 dqb_bsoftlimit; /* preferred limit on disk blks */<br> __u32 dqb_curblocks; /* current block count */<br> __u32 dqb_ihardlimit; /* absolute limit on allocated inodes */<br> __u32 dqb_isoftlimit; /* preferred inode limit */<br> __u32 dqb_curinodes; /* current # allocated inodes */<br> time_t dqb_btime; /* time limit for excessive disk use */<br> time_t dqb_itime; /* time limit for excessive inode use */<br>};<br>参考<font color=#000099><b>read_dquot</b></font>,<font color=#000099><b>write_dquot</b></font>.<br><br><font size=5><b>4.从quot文件中读出数据后存在一个hash中,以(kdev_t,id,type)为索引</b></font><br><br><font color=#3333ff>进入hash表: dget的时候,如果是新分配的dqota就进入hash表<br>撤出hash表: dquot只在重新被使用的时候,或者quot关闭(invalid)的时候才从hash中删除.并且重用的时候仅仅是临时删除<br>马上又加进来.<br></font><br> id: gid 或者uid, type:group quot或者user quot<br>参考函数:<br>static struct dquot *<font color=#000099><b>dqget</b></font>(struct super_block *sb, unsigned int id, short type)<br>{<br> unsigned int hashent = hashfn(sb->s_dev, id, type);<br> struct dquot *dquot, *empty = NULL;<br> struct quota_mount_options *dqopt = sb_dqopt(sb);<br><br> if (!is_enabled(dqopt, type))<br> return(NODQUOT);<br><br>we_slept:<br> if ((dquot = find_dquot(hashent, sb->s_dev, id, type)) == NULL) {<font color=#3333ff> //从hash查找</font><br> if (empty == NULL) { <font color=#3333ff>//没有查到</font><br> dquot_updating[hashent]++;<br> empty = <font color=#000099><b>get_empty_dquot</b></font>(); //分配新的quot数据结构<br> if (!--dquot_updating[hashent])<br> wake_up(&update_wait);<br> goto we_slept; <font color=#3333ff>//竞争处理,一定从hash中找的的才算.</font><br> }<font color=#3333ff><br> //进行初始化</font><br> dquot = empty;<br> dquot->dq_id = id;<br> dquot->dq_type = type;<br> dquot->dq_dev = sb->s_dev;<br> dquot->dq_sb = sb;<br> /* hash it first so it can be found */<br> <b>hash_dquot</b>(dquot); <font color=#3333ff>//挂入hash表</font><br> <b>read_dquot</b>(dquot); <font color=#3333ff>//从磁盘读入配置</font><br> } else { <font color=#3333ff>//quot已经在hash的情况</font><br> if (!dquot->dq_count++) { <font color=#3366ff>//增加其引用计数,引用计数是0代表在free list中<br> //参考dqput, 当引用计数是0的时候并不从hash中摘除的<br></font> remove_free_dquot(dquot); <br> } else<br> dqstats.cache_hits++;<br> wait_on_dquot(dquot);<br> if (empty)<br> dqput(empty);<br> }<br><br> while (dquot_updating[hashent])<br> sleep_on(&update_wait);<br><br> if (!dquot->dq_sb) { /* Has somebody invalidated entry under us? */<br> /*<br> * Do it as if the quota was invalidated before we started<br> */<br> dqput(dquot);<br> return NODQUOT;<br> }<br> dquot->dq_referenced++;<br> dqstats.lookups++;<br><br> return dquot;<br>}<br><br><font size=5><b>5. quot管理: quot的list使用策略</b></font><br>注释说的比较清楚了:<br>/*<br> * <b>Dquot List Management:</b><br> * The quota code uses three lists for dquot management: the inuse_list,<br> * free_dquots, and dquot_hash[] array. <b>A single dquot structure may be</b><br style=FONT-WEIGHT:bold><b> * on all three lists, depending on its current state.</b><br> *<br> * All dquots are placed on the inuse_list when first created, and this<br> * list is used for the sync and invalidate operations, which must look<br> * at every dquot.<br> *<br> * <b>Unused dquots (dq_count == 0) are added to the free_dquots list</b> when<br> * freed, and this list is searched whenever we need an available dquot.<br> * Dquots are removed from the list as soon as they are used again, and<br> * nr_free_dquots gives the number of dquots on the list.<br> *<br> * Dquots with a specific identity (device, type and id) are placed on<br> * one of the dquot_hash[] hash chains. The provides an efficient search<br> * mechanism to lcoate a specific dquot.<br> */<br>hash表我们已经说过了. <br><font color=#3333ff>dquot最多只分配max_dquots个(1024, typical). 然后从不释放.为所有文件系统所共享.并且一旦分配就进入<br>inuse队列.<br>当引用计数降到0的时候,进入unused队列,等待重用.此时其磁盘部分已经写入quot文件.(见dqput).等待重用的dquot<br>还在hash中,如果重新命中就马上回复使用. (而引用计数不为0,必定不再unused队列)<br><br>quot的设计是采用一定量的内存(dquot),轮换使用,映射为不同的quot file中的具体数据.<br><br>quot尽力保持高引用度的dquot存在于hash中,所以可以看到为此所做的努力:<br><b>static struct dquot *find_best_candidate_weighted(void)</b><br style=FONT-WEIGHT:bold><b>static inline struct dquot *find_best_free(void)<br></b><font color=#000000><br>说明白这些后,关于quot的分配释放/hash的管理/重用策略等都说完了,相关函数在这些知识下很容易理解.就不再一一列举.<br></font></font><font color=#3333ff><font color=#000000>static void dqput(struct dquot *dquot) : 不说了... -:)<br><br><br></font></font><font color=#3333ff><font color=#000000><font size=5><b>6. lock/unlock</b></font><br>操作dquot的时候需要lock: 读写/分配释放inode block. 还有一个per dquot的sleep队列...<br>DQ_MOD: 不难理解修改过,需要同步到quot file, 写入文件后清除此标记....<br><br><br><font size=5><b>7.inode 的quota操作</b></font><br>参考quotaops.h.<br>下面就是所有的操作接口: 初始化(找到dquota写入inode:i_dquta),释放, 分配释放block/分配释放inode,transfer(chown).<br>/*<br> * Definitions of diskquota operations.<br> */<br>struct dquot_operations dquot_operations = {<br> dquot_initialize, /* mandatory */ <font color=#3333ff>初始化 inode->i_dquot,简单明了..</font><br> dquot_drop, /* mandatory */ <font color=#3333ff>dqput(inode->i_dquot),同上...</font><br> dquot_alloc_block,<br> dquot_alloc_inode,<br> dquot_free_block,<br> dquot_free_inode,<br> dquot_transfer <font color=#3333ff>/*chown的时候进行....*/</font><br>};<br>然后通过quotaops.h定义的宏来使用这个接口....<br>唯一值得注意的是<br>static struct dquot *<font color=#3333ff><b>dqduplicate</b></font>(struct dquot *dquot)<br>{<br> if (dquot == NODQUOT || !dquot->dq_sb)<br> return NODQUOT;<br> dquot->dq_count++;<br> wait_on_dquot(dquot);<br> if (!dquot->dq_sb) {<br> dquot->dq_count--;<br> return NODQUOT;<br> }<br> dquot->dq_referenced++;<br> dqstats.lookups++;<br> return dquot;<br>}<br>只是获取一个引用计数,同时考虑加锁的情况:参考函数 </font></font><font color=#3333ff><font color=#000000>dquot_alloc_block, dquot_alloc_inode,结合其流程,此操作不难理解的.<br><br><br></font></font><font color=#3333ff><font color=#000000><br><font size=5><b>8. Sync , quota on/off</b></font><br>其实主要的部分已经完了. 剩下的就是使能, 关闭,同步操作了.<br>同步: 看一下函数,就是写入quota<br>int <font color=#3333ff><b>sync_dquots</b></font>(kdev_t dev, short type)<br>{<br> struct dquot *dquot, *next, *ddquot;<br> int need_restart;<br><br>restart:<br> next = inuse_list;<br> need_restart = 0;<br> while ((dquot = next) != NULL) {<br> next = dquot->dq_next;<br> <font color=#3333ff>/*各种有效性检查*/</font><br> if (dev && dquot->dq_dev != dev)<br> continue;<br> if (type != -1 && dquot->dq_type != type)<br> continue;<br> if (!dquot->dq_sb) /* Invalidated? */<br> continue;<br> <b>if (!(dquot->dq_flags & (DQ_LOCKED | DQ_MOD)))</b><br style=FONT-WEIGHT:bold><b> continue;</b><br><br> if ((ddquot = dqduplicate(dquot)) == NODQUOT)<br> continue;<br> if (ddquot->dq_flags & DQ_MOD)<br> write_dquot(ddquot);<br> dqput(ddquot);<br> /* Set the flag for another pass. */<br> need_restart = 1; <font color=#3333ff>//可能会睡眠,如要重新扫描</font><br> }<br> /*<br> * If anything blocked, restart the operation<br> * to ensure we don't miss any dquots.<br> */ <br> if (need_restart)<br> goto restart;<br><br> dqstats.syncs++;<br> return(0);<br>}<br>下面所有的函数都是为quota on/off准备的:<br>void <font color=#000099><b>invalidate_dquots</b></font>(kdev_t dev, short type) <font color=#3366ff>//说实话这个函数和同步差不多,只是要等待已经lock的quota而已</font><br>{ <font color=#3333ff>//invalid 操作必须完成(on/off),所以要等待->写入磁盘->重新初始化</font><br> .........<br>restart:<br> next = inuse_list; /* Here it is better. Otherwise the restart doesn't have any sense ;-) */<br> need_restart = 0;<br> while ((dquot = next) != NULL) {<br> ......<br> <b>if (dquot->dq_flags & DQ_LOCKED) {</b><br style=FONT-WEIGHT:bold><b> __wait_on_dquot(dquot);</b><br style=FONT-WEIGHT:bold><br style=FONT-WEIGHT:bold><b> /* Set the flag for another pass. */</b><br style=FONT-WEIGHT:bold><b> need_restart = 1;</b><br> /*<br> * Make sure it's still the same dquot.<br> */<br> if (dquot->dq_dev != dev)<br> continue;<br> if (dquot->dq_type != type)<br> continue;<br> if (!dquot->dq_sb)<br> continue;<br> }<br> /*<br> * Because inodes needn't to be the only holders of dquot<br> * the quota needn't to be written to disk. So we write it<br> * ourselves before discarding the data just for sure...<br> */<br> if (dquot->dq_flags & DQ_MOD && dquot->dq_sb)<br> {<br> write_dquot(dquot);<br> need_restart = 1; /* We slept on IO */<br> }<br> clear_dquot(dquot);<br> }<br> /*<br> * If anything blocked, restart the operation<br> * to ensure we don't miss any dquots.<br> */ <br> if (need_restart)<br> goto restart;<br>}<br><br><font color=#3333ff><br><b>add_dquot_ref</b></font> : quota on的时候, 遍历使能的quota的这个文件系统所有已经打开的文件,挂接一个dquot到inode->i_dquot<br><font color=#3333ff><br><br><br><b>remove_dquot_ref:这个函数在inode.c</b></font> quota off的时候,复杂性在于必须遍历所有inode,与上面</font></font><font color=#3333ff><font color=#000000><font color=#3333ff><b>add_dquot_ref</b></font></font></font><font color=#3333ff><font color=#000000> 有所不<br>同,并且将dqput分成两个步骤来做:<br> remove_inode_dquot_ref : </font></font><font color=#3333ff><font color=#000000><font color=#3333ff><b>remove_dquot_ref </b>is caller, 和dqput相比,不写入文件,仅仅减少引用计数+放到一个临时<br> 链表<br> put_dquot_list :遍历临时链表做dqput操作.<br><br><font color=#000000>说完了这些, dquot 的on off 函数反到根本不比再说了.</font><br><font color=#000000><br><font size=5><b>9.dquota的配置函数</b></font><br><br>这个好像不用说吧............,bye (函数虽多,配置而已,重要,但是理解他们的方式不再配置本身....)<br></font><br><br></font></font></font><br><br></pre>
</td>
</tr>
</tbody>
</table></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -