📄 035_fs_buffer_c.html
字号:
#define<font color=#006600> BUF_CLEAN</font> 0<br>
#define <font color=#006600>BUF_LOCKED </font>
1 /* Buffers scheduled for write */<br>
#define<font color=#006600> BUF_DIRTY</font>
2 /* Dirty buffers, not yet scheduled for write */<br>
#define <font color=#006600>BUF_PROTECTED</font>
3 /* Ramdisk persistent storage */<br>
#define NR_LIST 4<br>
static void <font color=#006600>__insert_into_lru_list</font>(struct
buffer_head * bh, int blist)<br>
static void <font color=#009900>__remove_from_lru_list</font>(struct
buffer_head * bh, int blist)<br>
<br>
static void <font color=#006600>__refile_buffer</font>(struct
buffer_head *bh)<br>
void <font color=#009900>refile_buffer</font>(struct buffer_head *bh)<br>
static __inline__ void <font color=#006600>__mark_dirty</font>(struct
buffer_head *bh)<br>
void <font color=#006600>__mark_buffer_dirty</font>(struct buffer_head
*bh)<br>
static inline void <font color=#006600>__mark_buffer_clean</font>(struct
buffer_head *bh)<br>
static inline void <font color=#006600>mark_buffer_clean</font>(struct
buffer_head * bh)<br>
static inline void
<font color=#006600>__mark_buffer_protected</font>(struct buffer_head
*bh)<br>
static inline void
<font color=#006600>mark_buffer_protected</font>(struct buffer_head *
bh)<br>
<br>
这些mark函数当然是标记buffer 的各种状态,然后通过
refile_buffer在各种类型的lru队列间移动。比较简单,就算是考虑的同步和互斥<br>
啥的也不能算作是复杂吧?<br>
<br>
<br>
<b>有时需要一些打包函数</b>,将buffer head 同时加入hash 和lru队列。<br>
static void <font color=#006600>__remove_from_queues</font>(struct
buffer_head *bh)<br>
static void <font color=#006600>__insert_into_queues</font>(struct
buffer_head *bh)<br>
<font color=#006600><br>
__refile_buffer<font color=#000000>
中有个</font></font><font color=#006600><font color=#000000>
remove_inode_queue(bh) 的操作值得注意一下。</font></font><font color=#006600><br>
<font color=#000000>/*<br>
* A buffer may need to be moved from one buffer list to another<br>
* (e.g. in case it is not shared any more). Handle this.<br>
*/<br>
static void __refile_buffer(struct buffer_head *bh)<br>
{<br>
int dispose = BUF_CLEAN;<br>
if (buffer_locked(bh))<br>
dispose = BUF_LOCKED;<br>
if (buffer_dirty(bh))<br>
dispose = BUF_DIRTY;<br>
if (buffer_protected(bh))<br>
dispose = BUF_PROTECTED;<br>
if (dispose != bh->b_list) {<br>
__remove_from_lru_list(bh,
bh->b_list);<br>
bh->b_list = dispose;<br>
<b> if (dispose ==
BUF_CLEAN)</b><br style=FONT-WEIGHT:bold>
<b>
</b><font color=#006600><b>
remove_inode_queue</b></font><b>(bh);</b><br>
__insert_into_lru_list(bh,
dispose);<br>
}<br>
}</font><br>
i<font color=#000000>node 中有个</font>inode->i_dirty_buffers
<font color=#000000>记录了这个inode中所有dirty的数据。稍后我们再分析这个dirty的数据是什么:元数据还是文件<br>
数据。</font><br>
</font>/* The caller must have the lru_list lock before calling the<br>
remove_inode_queue functions. */<br>
static void __remove_inode_queue(struct buffer_head *bh)<br>
{<br>
bh->b_inode = NULL;<br>
list_del(&bh->b_inode_buffers);<br>
}<br>
<br>
static inline void remove_inode_queue(struct buffer_head *bh)<br>
{<br>
<b> if (bh->b_inode) //可以看出,并不是每个buffer
都和一个inode 相对应的,只有以部分才有.</b><br>
__remove_inode_queue(bh);<br>
}<br>
int inode_has_buffers(struct inode *inode);//这个简单。。<br>
<br>
到底什么buffer才有inode与之对应,等分析万buffer cache的创建就会清楚了。<br>
我们先来看看buffer cache 的创建,藉此研究buffer cache 中的内容以及buffer cache
和系统其他几个部分之间的关系:<br>
<br>
<div style=TEXT-ALIGN:center>
<b>3)buffer cache 的创建与buffer head 的回收<br>
<br>
</b>
</div>
<b> </b> 实际上,<b>有两种类型的buffer_head
存在于系统中</b>,<b>一种存在于buffer cache</b>, 存在于buffer cache 中的 buffer(head)<br>
必然存在于lur list。<font color=#990000><b>这中类型的buffer 其唯一的分配途径就是</b></font>
<font color=#006600><b>getblk</b></font>, 然后通过bread(kdev_t dev, int
block, int size)被广泛用于读取文件的元数据:<br>
struct buffer_head * <font color=#006600><b>getblk</b></font>(kdev_t
dev, int block, int size)<br>
{<br>
....<br>
repeat:<br>
spin_lock(&lru_list_lock);<br>
write_lock(&hash_table_lock);<br>
bh = __get_hash_table(dev, block, size); <b>//look up
in hash first </b><br>
if (bh)<br>
goto out; <b>//找到就简单了</b><br>
<br>
isize = BUFSIZE_INDEX(size);<br>
spin_lock(&free_list[isize].lock);<br>
bh = free_list[isize].list; <b>//尝试在free
list 中分配一个</b><br>
if (bh) {<br>
__remove_from_free_list(bh,
isize);<br>
atomic_set(&bh->b_count,
1);<br>
}<br>
spin_unlock(&free_list[isize].lock);<br>
<br>
/*<br>
* OK, FINALLY we know that this buffer is the
only one of<br>
* its kind, we hold a reference (b_count>0),
it is unlocked,<br>
* and it is clean.<br>
*/<br>
if (bh) {<br>
init_buffer(bh, NULL, NULL);<br>
bh->b_dev = dev;<br>
bh->b_blocknr = block;<br>
bh->b_state = 1 <<
BH_Mapped; <b>//mapped buffer 已经有设备上的sector和之相对应</b><br>
<br>
/* Insert the buffer into the
regular lists */<br>
__insert_into_queues(bh);
<b>//进入hash 和 lru队列</b><br>
out:<br>
write_unlock(&hash_table_lock);<br>
spin_unlock(&lru_list_lock);<br>
touch_buffer(bh);<br>
return bh;<br>
}<br>
<br>
/*<br>
* If we block while refilling the free list,
somebody may<br>
* create the buffer first ... search the hashes
again.<br>
*/<br>
write_unlock(&hash_table_lock);<br>
spin_unlock(&lru_list_lock);<br>
<b> refill_freelist</b>(size); <b>
//分配失败的话,重新分配一批buffer 进来,再试</b><br>
goto repeat;<br>
}<br>
/*<br>
* We used to try various strange things. Let's not.<br>
* We'll just try to balance dirty buffers, and possibly<br>
* launder some pages.<br>
*/<br>
static void <b>refill_freelist</b>(int size)<br>
{<br>
balance_dirty(NODEV); // buffer 的回收策略,后面分析<br>
if (free_shortage()) //看看空闲物理页面是否足够
(以前分析过这个函数...)<br>
page_launder(GFP_BUFFER, 0);
//不够的话先释放一些buffer出来<br>
grow_buffers(size); //然后再创建buffer 到free list中<br>
}<br>
static int <b>grow_buffers</b>(int size)<br>
{<br>
....<br>
page = alloc_page(GFP_BUFFER); //分配页面<br>
if (!page)<br>
goto out;<br>
LockPage(page);<br>
bh = create_buffers(page, size, 0);
<b>//创建buffer,看看前面的概念,就是建立三元组(buffer_head, page, data buffer)</b><br>
<br>
insert_point = free_list[isize].list;<br>
.......<br>
free_list[isize].list = bh; <b>
//insert 到free list</b><br>
spin_unlock(&free_list[isize].lock);<br>
<br>
page->buffers = bh;<br>
page->flags &= ~(1 << PG_referenced);<br>
lru_cache_add(page);
<b><font color=#ff0000>//注意这里把page加入page cache的lru队列</font></b><br>
......<br>
}<br>
<font color=#cc0000>为啥吧page加入page cache的lru队列?</font>目的是让page cache
帮助进行buffer
head的老化回收:你注意到__put_unused_buffer_head的话,会发现只有<font color=#006600><b>try_to_free_buffers</b></font>才会调用这个函数(brw_kiovec也调用,但是不进入"主干",大部分buffer走不到那个分支上),秘密就在这里.
算是知道为啥page cache 和buffer cache纠缠不清了,真的是一个阴阳鱼啊.<br>
<br>
static struct buffer_head * create_buffers(struct page * page, unsigned
long size, int async) //略过<br>
<br>
<font color=#660000><b>另外一种buffer,并不存在于buffer cache
中</b>,</font><b><font color=#660000>仅仅作为磁盘rw的中介</font>,主要的创建接口函数</b>:<br>
static void
<font color=#006600><b>create_empty_buffers</b></font>(struct page
*page, kdev_t dev, unsigned long blocksize)<br>
create_empty_buffers为在给定的page上建立buffer,只是没有将buffer
映射的具体的磁盘块上,就是unmaped的buffer具体的映射操作交给具体的文件系统来处理。待会看个例子。<br>
<font color=#006600> create_empty_buffers</font>
为各种具体文件系统的文件读写(非元数据)提供一个和磁盘驱动交互的bh序列(把这个page切割成buffer),<br>
是文件系统和磁盘驱动交换数据的具体形式。据传,2.6系统中取消了buffer cache,仅仅保留这种类型的buffer,蜕变成一个io
entry。<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -