目录
buffer缓冲区介绍
高速缓冲区在整个五路内存中所处的位置
缓冲区划分,分为缓冲区头和缓冲区块
缓冲区双向链表结构
内核访问流程
wait_on_buffer
等待指定缓冲区解锁
static inline void wait_on_buffer(struct buffer_head * bh)
{
cli();//关中断
//如果上锁,则进程进入睡眠,等待解锁
while (bh->b_lock)
sleep_on(&bh->b_wait);
sti();//开中断
}
sys_sync
系统调用,数据同步,sync命令
int sys_sync(void)
{
int i;
struct buffer_head * bh;
//将i节点写入高速缓冲
sync_inodes(); /* write out inodes into buffers */
bh = start_buffer;
//扫描所有高速缓冲区,对已被修改的缓冲块产生写盘请求,将缓冲中数据与设备同步
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
wait_on_buffer(bh);//等待缓冲区解锁
if (bh->b_dirt)
ll_rw_block(WRITE,bh);//写设备块
}
return 0;
}
sync_dev
对指定设备进行高速缓冲数据与设备数据同步操作
int sync_dev(int dev)
{
int i;
struct buffer_head * bh;
bh = start_buffer;
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
if (bh->b_dev != dev)
continue;
wait_on_buffer(bh);
if (bh->b_dev == dev && bh->b_dirt)
ll_rw_block(WRITE,bh);
}
sync_inodes();
bh = start_buffer;
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
if (bh->b_dev != dev)
continue;
wait_on_buffer(bh);
if (bh->b_dev == dev && bh->b_dirt)
ll_rw_block(WRITE,bh);
}
return 0;
}
invalidate_buffers
使指定设备在高速缓冲区中的数据无效
void inline invalidate_buffers(int dev)
{
int i;
struct buffer_head * bh;
bh = start_buffer;
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
if (bh->b_dev != dev)
continue;
wait_on_buffer(bh);
if (bh->b_dev == dev)
bh->b_uptodate = bh->b_dirt = 0;
}
}
check_disk_change
检查磁盘是否更换,如果已更换就使对应高速缓冲区无效
/*
* This routine checks whether a floppy has been changed, and
* invalidates all buffer-cache-entries in that case. This
* is a relatively slow routine, so we have to try to minimize using
* it. Thus it is called only upon a 'mount' or 'open'. This
* is the best way of combining speed and utility, I think.
* People changing diskettes in the middle of an operation deserve
* to loose :-)
*
* NOTE! Although currently this is only for floppies, the idea is
* that any additional removable block-device will use this routine,
* and that mount/open needn't know that floppies/whatever are
* special.
*/
void check_disk_change(int dev)
{
int i;
//是否为软盘
if (MAJOR(dev) != 2)
return;
//测试对应软盘是否已更换,没有则退出
if (!floppy_change(dev & 0x03))
return;
//如果更换了,释放该对应设备的i节点位图和逻辑块位图所占的高速缓冲区
for (i=0 ; i<NR_SUPER ; i++)
if (super_block[i].s_dev == dev)
put_super(super_block[i].s_dev);
invalidate_inodes(dev);
invalidate_buffers(dev);
}
hash函数定义
hash函数(设备号^逻辑块号)Mod 307
#define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
#define hash(dev,block) hash_table[_hashfn(dev,block)]
remove_from_queues
从hash队列和空闲缓冲队列中移走指定的缓冲区
static inline void remove_from_queues(struct buffer_head * bh)
{
/* remove from hash-queue */
if (bh->b_next)
bh->b_next->b_prev = bh->b_prev;
if (bh->b_prev)
bh->b_prev->b_next = bh->b_next;
if (hash(bh->b_dev,bh->b_blocknr) == bh)
hash(bh->b_dev,bh->b_blocknr) = bh->b_next;
/* remove from free list */
if (!(bh->b_prev_free) || !(bh->b_next_free))
panic("Free block list corrupted");
bh->b_prev_free->b_next_free = bh->b_next_free;
bh->b_next_free->b_prev_free = bh->b_prev_free;
if (free_list == bh)
free_list = bh->b_next_free;
}
insert_into_queues
将指定缓冲区插入空闲链表尾并放入hash队列中
static inline void insert_into_queues(struct buffer_head * bh)
{
/* put at end of free list */
bh->b_next_free = free_list;
bh->b_prev_free = free_list->b_prev_free;
free_list->b_prev_free->b_next_free = bh;
free_list->b_prev_free = bh;
/* put the buffer in new hash-queue if it has a device */
bh->b_prev = NULL;
bh->b_next = NULL;
if (!bh->b_dev)
return;
bh->b_next = hash(bh->b_dev,bh->b_blocknr);
hash(bh->b_dev,bh->b_blocknr) = bh;
bh->b_next->b_prev = bh;
}
find_buffer
在高速缓冲区中寻找指定设备和指定块的缓冲区
static struct buffer_head * find_buffer(int dev, int block)
{
struct buffer_head * tmp;
for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)
if (tmp->b_dev==dev && tmp->b_blocknr==block)
return tmp;
return NULL;
}
get_hash_table
获取hash_table
/*
* Why like this, I hear you say... The reason is race-conditions.
* As we don't lock buffers (unless we are readint them, that is),
* something might happen to it while we sleep (ie a read-error
* will force it bad). This shouldn't really happen currently, but
* the code is ready.
*/
struct buffer_head * get_hash_table(int dev, int block)
{
struct buffer_head * bh;
for (;;) {
//在高速缓冲区中寻找给定设备和指定块缓冲区
if (!(bh=find_buffer(dev,block)))
return NULL;
//引用+1,等待缓冲区解锁
bh->b_count++;
wait_on_buffer(bh);
//经过睡眠状态,有必要再验证该缓冲区块的正确性
if (bh->b_dev == dev && bh->b_blocknr == block)
return bh;
//如果发生改变,撤销引用计数,重新寻找
bh->b_count--;
}
}
getblk
获取block
/*
* Ok, this is getblk, and it isn't very clear, again to hinder
* race-conditions. Most of the code is seldom used, (ie repeating),
* so it should be much more efficient than it looks.
*
* The algoritm is changed: hopefully better, and an elusive bug removed.
*/
#define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
struct buffer_head * getblk(int dev,int block)
{
struct buffer_head * tmp, * bh;
repeat:
//搜索hash表,指定块已经在高速缓冲中,返回
if ((bh = get_hash_table(dev,block)))
return bh;
//扫描空闲数据块链表,寻找空闲缓冲区
//tmp指向空闲链表的第一个空闲缓冲区头
tmp = free_list;
do {
// 如果该缓冲区被引用了,则扫描下一项
if (tmp->b_count)
continue;
//如果缓冲区头指针为空或者tmp所指缓冲头的标志
if (!bh || BADNESS(tmp)<BADNESS(bh)) {
bh = tmp;
if (!BADNESS(tmp))
break;
}
/* and repeat until we find something good */
} while ((tmp = tmp->b_next_free) != free_list);
// 如果所有缓冲区都正被使用,则睡眠,等待有空闲的缓冲区可用
if (!bh) {
sleep_on(&buffer_wait);
goto repeat;
}
//等待解锁
wait_on_buffer(bh);
if (bh->b_count)
goto repeat;
//如果该缓冲区被修改,将数据写入盘
while (bh->b_dirt) {
sync_dev(bh->b_dev);
wait_on_buffer(bh);
if (bh->b_count)
goto repeat;
}
/* NOTE!! While we slept waiting for this block, somebody else might */
/* already have added "this" block to the cache. check it */
if (find_buffer(dev,block))
goto repeat;
/* OK, FINALLY we know that this buffer is the only one of it's kind, */
/* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */
bh->b_count=1;
bh->b_dirt=0;
bh->b_uptodate=0;
remove_from_queues(bh);
bh->b_dev=dev;
bh->b_blocknr=block;
insert_into_queues(bh);
return bh;
}
brelse
释放指定缓冲区,等待缓冲区解锁,引用计数减1
void brelse(struct buffer_head * buf)
{
if (!buf)
return;
wait_on_buffer(buf);
if (!(buf->b_count--))
panic("Trying to free free buffer");
wake_up(&buffer_wait);
}
bread
读取指定设备的数据库
*
* bread() reads a specified block and returns the buffer that contains
* it. It returns NULL if the block was unreadable.
*/
struct buffer_head * bread(int dev,int block)
{
struct buffer_head * bh;
//在高速缓冲区中申请一块缓冲区
if (!(bh=getblk(dev,block)))
panic("bread: getblk returned NULL\n");
//如果缓冲区数据有效,直接使用
if (bh->b_uptodate)
return bh;
//否则使用产生读块设备请求,等待缓冲区解锁
ll_rw_block(READ,bh);
wait_on_buffer(bh);
//如果缓冲区更新,返回
if (bh->b_uptodate)
return bh;
//失败
brelse(bh);
return NULL;
}
bread_page
一次读取4个缓冲区内容到内存指定的地址
#define COPYBLK(from,to) \
__asm__("cld\n\t" \
"rep\n\t" \
"movsl\n\t" \
::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \
)
/*
* bread_page reads four buffers into memory at the desired address. It's
* a function of its own, as there is some speed to be got by reading them
* all at the same time, not waiting for one to be read, and then another
* etc.
*/
void bread_page(unsigned long address,int dev,int b[4])
{
struct buffer_head * bh[4];
int i;
for (i=0 ; i<4 ; i++)
if (b[i]) {
//取高速缓冲中指定设备和块号缓冲区,如果该缓冲区数据无效则产生读设备请求
if ((bh[i] = getblk(dev,b[i])))
if (!bh[i]->b_uptodate)
ll_rw_block(READ,bh[i]);
} else
bh[i] = NULL;
//将4块缓冲区上的内容顺序复制到指定地址处
for (i=0 ; i<4 ; i++,address += BLOCK_SIZE)
if (bh[i]) {
wait_on_buffer(bh[i]);
if (bh[i]->b_uptodate)
COPYBLK((unsigned long) bh[i]->b_data,address);
brelse(bh[i]);
}
}
breada
breada可以像bread一样,但会预读一些块,从指定设备读取指定的一些块,成功时返回第一块的缓冲区头指针
/*
* Ok, breada can be used as bread, but additionally to mark other
* blocks for reading as well. End the argument list with a negative
* number.
*/
struct buffer_head * breada(int dev,int first, ...)
{
va_list args;
struct buffer_head * bh, *tmp;
va_start(args,first);
if (!(bh=getblk(dev,first)))
panic("bread: getblk returned NULL\n");
if (!bh->b_uptodate)
ll_rw_block(READ,bh);
while ((first=va_arg(args,int))>=0) {
tmp=getblk(dev,first);
if (tmp) {
if (!tmp->b_uptodate)
ll_rw_block(READA,bh);
tmp->b_count--;
}
}
va_end(args);
wait_on_buffer(bh);
if (bh->b_uptodate)
return bh;
brelse(bh);
return (NULL);
}
buffer_init
缓冲区初始化,系统有16MB内存,缓冲区末端设置为4MB,内存为8MB,缓冲区末端设置为2MB
void buffer_init(long buffer_end)
{
struct buffer_head * h = start_buffer;
void * b;
int i;
//如果缓冲区高端等于1MB,则由于640KB-10MB被显存和BIOS占用,因此实际可用缓冲区内存高端应该为640KB
if (buffer_end == 1<<20)
b = (void *) (640*1024);
else
b = (void *) buffer_end;
while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) {
h->b_dev = 0;
h->b_dirt = 0;
h->b_count = 0;
h->b_lock = 0;
h->b_uptodate = 0;
h->b_wait = NULL;
h->b_next = NULL;
h->b_prev = NULL;
h->b_data = (char *) b;
h->b_prev_free = h-1;
h->b_next_free = h+1;
h++;
NR_BUFFERS++;
//如果地址b递减到等于1MB,则跳过384KB,让b指向地址0xA0000(640KB)处
if (b == (void *) 0x100000)
b = (void *) 0xA0000;
}
h--;
free_list = start_buffer;
free_list->b_prev_free = h;
h->b_next_free = free_list;
for (i=0;i<NR_HASH;i++)
hash_table[i]=NULL;
}