都一个月没更新了,因为放了两个多星期假,然后回来也准备了下实验室年会的报告,现在终于事情都完了,回归正途~
在看关于将buffer置为脏页的时候,我们是看到如下,也就是根本跟不进他在哪里
当然,我们从他的含义上是知道他是做了什么事情的,但是就是心里痒痒不知道他究竟是怎么做的?为什么不是以一个函数出现的呢?(因为如果是函数当然可以跟进去),那么一种情况就是可能是以宏的形式出现。那么我们就尝试在跟buffer有关的文件中找一下。可以在/include/linux/buffer_head.h中在对struct buffer_head的定义后有对buffer_head中的b_state字段做了解释的:
enum bh_state_bits {
BH_Uptodate, /* Contains valid data */
BH_Dirty, /* Is dirty */
BH_Lock, /* Is locked */
BH_Req, /* Has been submitted for I/O */
BH_Uptodate_Lock,/* Used by the first bh in a page, to serialise
* IO completion of other buffers in the page
*/
BH_Mapped, /* Has a disk mapping */
BH_New, /* Disk mapping was newly created by get_block */
BH_Async_Read, /* Is under end_buffer_async_read I/O */
BH_Async_Write, /* Is under end_buffer_async_write I/O */
BH_Delay, /* Buffer is not yet allocated on disk */
BH_Boundary, /* Block is followed by a discontiguity */
BH_Write_EIO, /* I/O error on write */
BH_Ordered, /* ordered write */
BH_Eopnotsupp, /* operation not supported (barrier) */
BH_Unwritten, /* Buffer is allocated on disk but not written */
BH_Quiet, /* Buffer Error Prinks to be quiet */
BH_Meta, //fangxj2
BH_PrivateStart,/* not a state bit, but the first bit available
* for private allocation by other entities
*/
};
该字段表示了buffer的状态的,在后面我们又看到:
#define BUFFER_FNS(bit, name) \
static inline void set_buffer_##name(struct buffer_head *bh) \
{ \
set_bit(BH_##bit, &(bh)->b_state); \
} \
static inline void clear_buffer_##name(struct buffer_head *bh) \
{ \
clear_bit(BH_##bit, &(bh)->b_state); \
} \
static inline int buffer_##name(const struct buffer_head *bh) \
{ \
return test_bit(BH_##bit, &(bh)->b_state); \
}
/*
* test_set_buffer_foo() and test_clear_buffer_foo()
*/
#define TAS_BUFFER_FNS(bit, name) \
static inline int test_set_buffer_##name(struct buffer_head *bh) \
{ \
return test_and_set_bit(BH_##bit, &(bh)->b_state); \
} \
static inline int test_clear_buffer_##name(struct buffer_head *bh) \
{ \
return test_and_clear_bit(BH_##bit, &(bh)->b_state); \
} \
很有意思吧?首先我们要先知道 在#define中“##”这是表示字符的连接的,就像 #define M(x) a##x+#c 如果我们使用 M (mm) 那么在预编译时候是变成了 ammc 。好的,稍微有点经验的应该明白 那这里用该就是 当我们调用了set_bufer_dirty 那就是将这上面定义的set_buffer_##name 中的name用我们的dirty替换为dirty.那么为什么调用时候不是调用宏BUFFER_FNS呢?另外,为什么在函数体中
set_bit(BH_##bit, &(bh)->b_state);
为什么不是 BH_##name 而是BH_##bit呢? 确实,这里用name的话也是不对,看上面的你枚举类型如果这里是##name的话那么我应该是调用set_buffer_Dirty而不是set_buffer_dirty。那么肯定还存在一个bit与name的转化换,将name如果是dirty转成Dirty。
所以接着又看到了:
BUFFER_FNS(Uptodate, uptodate)
BUFFER_FNS(Dirty, dirty)
TAS_BUFFER_FNS(Dirty, dirty)
BUFFER_FNS(Lock, locked)
BUFFER_FNS(Req, req)
TAS_BUFFER_FNS(Req, req)
BUFFER_FNS(Mapped, mapped)
BUFFER_FNS(New, new)
BUFFER_FNS(Async_Read, async_read)
BUFFER_FNS(Async_Write, async_write)
BUFFER_FNS(Delay, delay)
BUFFER_FNS(Boundary, boundary)
BUFFER_FNS(Write_EIO, write_io_error)
BUFFER_FNS(Ordered, ordered)
BUFFER_FNS(Eopnotsupp, eopnotsupp)
BUFFER_FNS(Unwritten, unwritten)
所以这时候结果出来了!!
我们知道宏在预编译时候要进行替换,那么在预编译时候,上面的这一系列宏都被展开。那么结果应该就是对于第一个BUFFER_FNS(Update,update) 就是将上面的三个函数的bit用Update替换,对name用update替换。也就是:
static inline void set_buffer_update(struct buffer_head *bh) \
{ \
set_bit(BH_Update, &(bh)->b_state); \
} \
static inline void clear_buffer_update (struct buffer_head *bh) \
{ \
clear_bit(BH_Update, &(bh)->b_state); \
} \
static inline int buffer_update (const struct buffer_head *bh) \
{ \
return test_bit(BH_Update, &(bh)->b_state); \
}
同样,同样对于第二个,BUFFER_FNS(Dirty,dirty) 也一样,变成了:
static inline void set_buffer_dirty(struct buffer_head *bh) \
{ \
set_bit(BH_Dirty, &(bh)->b_state); \
} \
static inline void clear_buffer_dirty (struct buffer_head *bh) \
{ \
clear_bit(BH_Dirty, &(bh)->b_state); \
} \
static inline int buffer_dirty (const struct buffer_head *bh) \
{ \
return test_bit(BH_Dirty, &(bh)->b_state); \
}
依次类推,那么在预编译后,实际上根据这个BUFFER_FNS也就产生了 3*15=45个函数的!!
是不是有一次对linux 代码的巧妙性又叹服了一次。这样既使得代码量少了(所以说高质量的代码让你用几行的代码实现人家重复的40几行的功能),另一个就是扩展性好。如果我在Buffer_head的state中想要增加其他的信息,我只要在他的枚举中加入,例如我加入一个BH_Meta,然后再加入一个BUFFER_FND(Meta,meta) 就OK了!