荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: 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软件 网络书店