荔园在线
荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀
[回到开始]
[上一篇][下一篇]
发信人: jjk (pq), 信区: Linux
标 题: [转载] 分析块设备缓冲区结构(转寄)
发信站: 荔园晨风BBS站 (Thu Nov 29 06:45:37 2001), 转信
【 以下文字转载自 jjk 的信箱 】
【 原文由 jjksam@smth.org 所发表 】
发信人: Pasific (饮水思源), 信区: KernelTech
标 题: 分析块设备缓冲区结构
发信站: BBS 水木清华站 (Wed Nov 28 23:27:44 2001)
转载自 www.linuxforum.net opera
块设备缓冲区用buffer_head结构描述,系统中有NR_SIZES种不同尺寸的缓冲区,每种缓
冲区的尺寸为512<
grow_buffers(blocksize);
在free_list[]上扩建一页块长为blocksize的备用缓冲区;
bh = create_buffers(page,blocksize,async);
创建块长为blocksize的buffer_head结构来描述页面page.
create_empty_buffers(page,dev,blocksize);
创建块长为blocksize,块设备为dev的buffer_head结构来描述页面page
bh = get_unused_buffer_head(async);
取备用的buffer_head结构,async=1允许进程暂时睡眠.
struct buffer_head {
struct buffer_head *b_next; 用于缓冲块索引的散列链
unsigned long b_blocknr; 该缓冲区在块设备上的块号
unsigned short b_size; 该缓冲区数据块尺寸
unsigned short b_list; 在lru_list[]中的序号,表示该缓冲区的使用状态.
kdev_t b_dev; 缓冲区所属的逻辑块设备
atomic_t b_count; 引用计数
kdev_t b_rdev; 所属的物理块设备
unsigned long b_state;
unsigned long b_flushtime;
struct buffer_head *b_next_free; 指向下一备用缓冲块
struct buffer_head *b_prev_free; 指向前一备用缓冲块
struct buffer_head *b_this_page; 指向同一页面的缓冲块,形成环形链表
struct buffer_head *b_reqnext; 用于块设备驱动程序
struct buffer_head **b_pprev; 用于缓冲块散列链
char * b_data; 指向缓冲块的数据区
struct page *b_page; 缓冲块的数据区所在的页面
void (*b_end_io)(struct buffer_head *bh, int uptodate);
void *b_private;
unsigned long b_rsector;
wait_queue_head_t b_wait;
struct inode * b_inode;
struct list_head b_inode_buffers;
};
#define NR_SIZES 7
; 这是一张以2为底的简易对数表,用于块长度到free_list[]索引的转换
static char buffersize_index[65] =
{-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1,
4, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1,
5, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1,
6};
#define BUFSIZE_INDEX(X) ((int) buffersize_index[(X)>>9])
#define MAX_BUF_PER_PAGE (PAGE_CACHE_SIZE / 512) 每页最多的缓冲块数(8)
#define NR_RESERVED (2*MAX_BUF_PER_PAGE)
#define MAX_UNUSED_BUFFERS NR_RESERVED+20 /* don't ever have more than this
number of unused buffer heads */
static struct buffer_head *lru_list[NR_LIST];
static int nr_buffers_type[NR_LIST]; 每种尺寸缓冲块的数量
static unsigned long size_buffers_type[NR_LIST]; 每种尺寸缓冲区的字节总数
static struct buffer_head * unused_list;
static int nr_unused_buffer_heads;
struct bh_free_head {
struct buffer_head *list;
spinlock_t lock;
};
static struct bh_free_head free_list[NR_SIZES];
static int grow_buffers(int size)
{
struct page * page;
struct buffer_head *bh, *tmp;
struct buffer_head * insert_point;
int isize;
if ((size & 511) || (size > PAGE_SIZE)) {
printk("VFS: grow_buffers: size = %d\n",size);
return 0;
}
page = alloc_page(GFP_BUFFER);
if (!page)
goto out;
LockPage(page);
bh = create_buffers(page, size, 0);
if (!bh)
goto no_buffer_head;
isize = BUFSIZE_INDEX(size);
spin_lock(&free_list[isize].lock);
insert_point = free_list[isize].list;
tmp = bh;
while (1) { 将页中的每一块缓冲区插入free_list[isize].list
if (insert_point) {
tmp->b_next_free = insert_point->b_next_free;
tmp->b_prev_free = insert_point;
insert_point->b_next_free->b_prev_free = tmp;
insert_point->b_next_free = tmp;
} else {
tmp->b_prev_free = tmp;
tmp->b_next_free = tmp;
}
insert_point = tmp;
if (tmp->b_this_page)
tmp = tmp->b_this_page;
else
break;
}
tmp->b_this_page = bh; 形成单向环形链表
free_list[isize].list = bh;
spin_unlock(&free_list[isize].lock);
page->buffers = bh; 表示该页与块设备缓冲区相关联
page->flags &= ~(1 << PG_referenced);
lru_cache_add(page); 将该页加入页面LRU链表
UnlockPage(page);
atomic_inc(&buffermem_pages);
return 1;
no_buffer_head:
UnlockPage(page);
page_cache_release(page); 释放alloc_page()分配的页面
out:
return 0;
}
static struct buffer_head * create_buffers(struct page * page, unsigned long
size, int async)
{
struct buffer_head *bh, *head;
long offset;
try_again:
head = NULL;
offset = PAGE_SIZE;
while ((offset -= size) >= 0) { 从页面的高端向低端分配地址
bh = get_unused_buffer_head(async);
if (!bh)
goto no_grow;
bh->b_dev = B_FREE; /* Flag as unused */
bh->b_this_page = head;
head = bh;
bh->b_state = 0;
bh->b_next_free = NULL;
bh->b_pprev = NULL;
atomic_set(&bh->b_count, 0);
bh->b_size = size;
set_bh_page(bh, page, offset);
bh->b_list = BUF_CLEAN;
bh->b_end_io = NULL;
}
return head;
/*
* In case anything failed, we just free everything we got.
*/
no_grow:
if (head) { 如果在分配中途失败,则撤消已有分配
spin_lock(&unused_list_lock);
do {
bh = head;
head = head->b_this_page;
__put_unused_buffer_head(bh);
} while (head);
spin_unlock(&unused_list_lock);
/* Wake up any waiters ... */
wake_up(&buffer_wait); 唤醒下文因wait_event()而睡眠的那些进程
}
/*
* Return failure for non-async IO requests. Async IO requests
* are not allowed to fail, so we have to wait until buffer heads
* become available. But we don't want tasks sleeping with
* partially complete buffers, so all were released above.
*/
if (!async)
return NULL;
/* We're _really_ low on memory. Now we just
* wait for old buffer heads to become free due to
* finishing IO. Since this is an async request and
* the reserve list is empty, we're sure there are
* async buffer heads in use.
*/
run_task_queue(&tq_disk);
/*
* Set our state for sleeping, then check again for buffer heads.
* This ensures we won't miss a wake_up from an interrupt.
*/
wait_event(buffer_wait, nr_unused_buffer_heads >= MAX_BUF_PER_PAGE);
; 如果nr_unused_buffer_heads >= MAX_BUF_PER_PAGE 则wait_event返回,否则睡眠
goto try_again;
}
static struct buffer_head * get_unused_buffer_head(int async)
{
struct buffer_head * bh;
spin_lock(&unused_list_lock);
if (nr_unused_buffer_heads > NR_RESERVED) {
bh = unused_list;
unused_list = bh->b_next_free;
nr_unused_buffer_heads--;
spin_unlock(&unused_list_lock);
return bh;
}
spin_unlock(&unused_list_lock);
/* This is critical. We can't swap out pages to get
* more buffer heads, because the swap-out may need
* more buffer-heads itself. Thus SLAB_BUFFER.
*/
if((bh = kmem_cache_alloc(bh_cachep, SLAB_BUFFER)) != NULL) {
memset(bh, 0, sizeof(*bh));
init_waitqueue_head(&bh->b_wait);
return bh;
}
/*
* If we need an async buffer, use the reserved buffer heads.
*/
if (async) {
spin_lock(&unused_list_lock);
if (unused_list) {
bh = unused_list;
unused_list = bh->b_next_free;
nr_unused_buffer_heads--;
spin_unlock(&unused_list_lock);
return bh;
}
spin_unlock(&unused_list_lock);
}
#if 0
/*
* (Pending further analysis ...)
* Ordinary (non-async) requests can use a different memory priority
* to free up pages. Any swapping thus generated will use async
* buffer heads.
*/
if(!async &&
(bh = kmem_cache_alloc(bh_cachep, SLAB_KERNEL)) != NULL) {
memset(bh, 0, sizeof(*bh));
init_waitqueue_head(&bh->b_wait);
return bh;
}
#endif
return NULL;
}
static void create_empty_buffers(struct page *page, kdev_t dev, unsigned lon
g blocksize)
{
struct buffer_head *bh, *head, *tail;
head = create_buffers(page, blocksize, 1);
if (page->buffers)
BUG();
bh = head;
do {
bh->b_dev = dev;
bh->b_blocknr = 0;
bh->b_end_io = NULL;
tail = bh;
bh = bh->b_this_page;
} while (bh);
tail->b_this_page = head;
page->buffers = head;
page_cache_get(page);
}
void set_bh_page (struct buffer_head *bh, struct page *page, unsigned long o
ffset)
{
bh->b_page = page;
if (offset >= PAGE_SIZE)
BUG();
if (PageHighMem(page))
/*
* This catches illegal uses and preserves the offset:
*/
bh->b_data = (char *)(0 + offset);
else
bh->b_data = page_address(page) + offset;
}
static __inline__ void __put_unused_buffer_head(struct buffer_head * bh)
{
if (bh->b_inode)
BUG();
if (nr_unused_buffer_heads >= MAX_UNUSED_BUFFERS) {
kmem_cache_free(bh_cachep, bh);
} else {
bh->b_blocknr = -1;
init_waitqueue_head(&bh->b_wait);
nr_unused_buffer_heads++;
bh->b_next_free = unused_list;
bh->b_this_page = NULL;
unused_list = bh;
}
}
--
※ 来源:·BBS 水木清华站 smth.org·[FROM: 166.111.167.138]
--
※ 转载:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.0.146]
[回到开始]
[上一篇][下一篇]
荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店