Linux0.11内核源码解析-buffer.c

文章详细介绍了Linux内核中缓冲区管理的相关函数,包括wait_on_buffer用于等待缓冲区解锁,sys_sync和sync_dev实现数据同步,invalidate_buffers使缓冲区数据无效,以及check_disk_change检查磁盘变化并处理。同时,文章还涉及到了hash函数、缓冲区的插入和移除等操作。
摘要由CSDN通过智能技术生成

目录

buffer缓冲区介绍

wait_on_buffer

sys_sync

sync_dev

invalidate_buffers

check_disk_change

hash函数定义

remove_from_queues

insert_into_queues

find_buffer

get_hash_table

getblk

brelse

bread

bread_page

breada

buffer_init

缓冲区管理函数之间的层次关系


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;
}	

缓冲区管理函数之间的层次关系

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值