当内核需要读或写一个单独的物理设备块时(例如一个超级块),必须检查所请求的块缓冲区是否已经在页高速缓存中。在页高速缓存中搜索指定的块缓冲区(由块设备描述符的地址bdev和逻辑块号nr表示)的过程分成三个步骤:
1. 获取一个指针,让它指向包含指定块的块设备的address_space对象(bdev->bd_inode->i_mapping)。
2. 获得设备的块大小(bdev->bd_block_size),并计算包含指定块的页索引。这需要在逻辑块号上进行位移操作。例如,如果块的大小是1024字节,每个缓冲区页包含四个块缓冲区,那么页的索引是nr/4。
3. 在块设备的基树中搜索缓冲区页。获得页描述符之后,内核访问缓冲区首部,它描述了页中块缓冲区的状态。
不过,实现的细节要更为复杂。为了提高系统性能,内核维持一个小磁盘高速缓存数组bh_lrus(每个CPU对应一个数组元素),即所谓的最近最少使用(LRU)块高速缓存。每个磁盘高速缓存有8个指针,指向被指定CPU最近访问过的缓冲区首部。对每个CPU数组的元素排序,使指向最后被使用过的那个缓冲区首部的指针索引为0。相同的缓冲区首部可能出现在几个CPU数组中(但是同一个CPU数组中不会有相同的缓冲区首部)。在LRU块高速缓存中每出现一次缓冲区首部,该缓冲区首部的使用计数器b_count就加1。
1 __find_get_block()函数
函数__find_get_block()的参数有:block_device描述符地址bdev,块号block和块大小size。函数返回页高速缓存中的块缓冲区对应的缓冲区首部的地址;如果不存在指定的块,就返回NULL:
struct buffer_head *
__find_get_block(struct block_device *bdev, sector_t block, int size)
{
struct buffer_head *bh = lookup_bh_lru(bdev, block, size);
if (bh == NULL) {
bh = __find_get_block_slow(bdev, block);
if (bh)
bh_lru_install(bh);
}
if (bh)
touch_buffer(bh);
return bh;
}
数本质上执行下面的操作:
1. 检查执行CPU的LRU块高速缓存数组中是否有一个缓冲区首部,其b_bdev、b_blocknr和b_size字段分别等于bdev、block和size:
static struct buffer_head *
lookup_bh_lru(struct block_device *bdev, sector_t block, int size)
{
struct buffer_head *ret = NULL;
struct bh_lru *lru;
int i;
check_irqs_on();
bh_lru_lock();
lru = &__get_cpu_var(bh_lrus);
for (i = 0; i < BH_LRU_SIZE