📄 buffer.c.txt
字号:
any question,send email to netxiong@263.net
相关文件:
/init/main.c
该文件提供了文件的buffer功能的大部分实现,同时也对块设备的buffer有效
****************************基本数据结构*************************
(1):#define NR_SIZES 7 //最多由7种buffer大小的类型512,1024,2048,4096,8192,34816,
(2):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};//hash函数的映射表,-1处不使用,0----6代表了7种类型
(3):#define BUFSIZE_INDEX(X) ((int) buffersize_index[(X)>>9])
#例如 4096>>9 = 8(1000)
#buffersize_index[8] = 3
(4):static struct buffer_head **hash_table; //hash数组,存储了所有的buffer
static rwlock_t hash_table_lock = RW_LOCK_UNLOCKED//链表的自旋锁
**********************************************************************
****************************各种内存连表的定义和函数*************************
(5):struct bh_free_head {
struct buffer_head *list;
spinlock_t lock;
};
(6):static struct bh_free_head free_list[NR_SIZES];
基本函数:
static void __remove_from_free_list(struct buffer_head * bh, int index)
#将bh代表的buffer从free_list buffer池中删除
#程序挺有意思,值得一看,很简单
#首先将bh从双向链表中删除,
#如果free_list指向的是bh,则将free_list指向bh的next buffer.
定义了lru数组,4个元素。(include/linux/fs.h)
BUF_CLEAN0,BUF_LOCKED,BUF_DIRTY,BUF_PROTECTED
static struct buffer_head *lru_list[NR_LIST]; //几种不同的buffer池
static int nr_buffers_type[NR_LIST]; //每一种相应的buffer池中的buffer数量
static unsigned long size_buffers_type[NR_LIST]; //每一种相应的池中的总量
static spinlock_t lru_list_lock = SPIN_LOCK_UNLOCKED;//链表的自旋锁
处理函数:
(1):static void __insert_into_lru_list(struct buffer_head * bh, int blist)
#将一个buffer插入倒blist类型的lru buffer池中
#将bh加入到 &lru_list[blist]的前面(后来的buffer加入到头部)
#nr_buffers_type[blist]++; //增加相应的buffer数目
#size_buffers_type[blist] += bh->b_size;//增加相应的buffer池的容量
(2):static void __remove_from_lru_list(struct buffer_head * bh, int blist)
(8):
static struct buffer_head * unused_list;
static int nr_unused_buffer_heads; //为使用的buffer_head的数目
static DECLARE_WAIT_QUEUE_HEAD(buffer_wait);
static spinlock_t unused_list_lock = SPIN_LOCK_UNLOCKED;//链表的自旋锁
***************************************************************************
************************基本宏************************************
(1):#define _hashfn(dev,block) \ //哈希映射函数
((((dev)<<(bh_hash_shift - 6)) ^ ((dev)<<(bh_hash_shift - 9))) ^ \
(((block)<<(bh_hash_shift - 6)) ^ ((block) >> 13) ^ \
((block) << (bh_hash_shift - 12))))
(2):#define hash(dev,block) hash_table[(_hashfn(HASHDEV(dev),block) & bh_hash_mask)]
#这两个宏的作用就是buffer池中取出相应的buffer。
******************************************************************
***************************基本函数*******************************
(1):void __init buffer_init(unsigned long mempages)
#这个函数再main.c中被调用,作用是为系统提供一系列的buffer.
#首先进行了一系列的计算
# hash_table = (struct buffer_head **)
__get_free_pages(GFP_ATOMIC, order);
#然后调用__get_free_pages进行页分配,从而产生了一个buffer池
#对hash_table的buffer池进行初始化。
#对free_list进行初始化
#对lru_list进行初始化
******************************************************************
****************************守护进程*******************************
(1):static int __init bdflush_init(void)
#初始化两个守护线程
# kernel_thread(bdflush, &sem, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
#kernel_thread(kupdate, &sem, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
(2):int kupdate(void *sem)
#更新进程
(3):int bdflush(void *sem)
#守护进程,无限循环,负责将dirty数据刷新倒设备上
#主要的函数调用是flushed = flush_dirty_buffers(0);
(4):static int flush_dirty_buffers(int check_flushtime)
#这个函数将实际上的dirty数据写倒设备上去
#bh = lru_list[BUF_DIRTY]; //我们值更新BUF_DIRTY的buffer.
#ll_rw_block(WRITE, 1, &bh); //这是最为关键的一步,将buffer中的数 //据写到块设备上去
*******************************************************************
**************************数据结构图********************************
hash_table
[0]------->buffer_head
[1]--->null
[…]
[n]--->null
[n+1]------>buffer_head
|<---------b_pprev
b_next--------->buffer_head
|<---------------b_pprev
[n+2]
[…]
free_list (使用b_next_free字段进行连接)
[0](512)--------->buffer_head-------->buffer_head
[1](1024)-------->buffer_head-------->buffer_head
[…]
(使用b_next_free字段进行连接)
unused_list------>buffer_head--------->buffer_head
********************************************************************
**************************getblk函数集******************************
(1):struct buffer_head * getblk(kdev_t dev, int block, int size) (2.4.2)
#函数的作用是从设备号dev和请求的块号block来得到所请求的buffer池入口
# bh = __get_hash_table(dev, block, size);//从hash表中取出buffer入口
# if (bh)
goto out; //如果能够找到,就到出口处
#否则bh = free_list[isize].list;//从free_list表中取出一个空闲的head
#if (bh) { //如果free_list表中还有buffer_head
__remove_from_free_list(bh, isize);
atomic_set(&bh->b_count, 1); //buffer_head的使用者加一
}//如果空闲的head表中又buffer_head,则把他从空闲表中删除并加一
#如果bh有效init_buffer(bh, NULL, NULL)//对bh进行初始化
# __insert_into_queues(bh); //加入到hash_table相应的入口中
#out:
touch_buffer(bh); //将buffer中的page位置位(?)
return bh; //返回bh
#如果没有相应的buffer_head
#refill_freelist(size) //从新对free_list进行处理,等待一个可用的free
#跳转到函数头,从新执行。
(2):static void refill_freelist(int size)
# balance_dirty(NODEV);//对dirty数据页进行平衡,计算是否要想设备写
# grow_buffers(size); //增加buffers。
(3):static int grow_buffers(int size)(2.4.2)
#在free_list[]上扩建一页块长为blocksize的备用缓冲区;
#page = alloc_page(GFP_BUFFER);//申请一个页
#bh = create_buffers(page, size, 0);//在页上生成一个size的缓冲区
#insert_point = 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;
}
(4):static struct buffer_head * create_buffers(struct page * page, unsigned long size, int async)
#创建块长为blocksize的buffer_head结构来描述页面page.
#bh = get_unused_buffer_head(async);//得到一个buffer_head
# if (!bh)
goto no_grow; //如果没有buffer_head,等待
#否则的话就对bh进行设置
#no_grow:
# wait_event(buffer_wait, nr_unused_buffer_heads >= MAX_BUF_PER_PAGE);//等待
():static struct buffer_head * get_unused_buffer_head(int async)
#得到一个空闲的buffer_head
():static __inline__ void __put_unused_buffer_head(struct buffer_head * bh)
#将buffer_head加入到unused_list中去
# if (nr_unused_buffer_heads >= MAX_UNUSED_BUFFERS)
kmem_cache_free(bh_cachep, bh);//如果buffer太多,释放
#否则 nr_unused_buffer_heads++;
bh->b_next_free = unused_list;
bh->b_this_page = NULL;
unused_list = bh;//将新buffer_head加入到unused_list中去
#注意:unused_list就是在这里被赋值的。无论unused_list是什么。
********************************************************************
************************缓存的申请和释放过程*************************
union bdflush_param {
struct {
int nfract; /* 脏块比例达到多少唤醒bdflush进程 */
int dummy1; /* old "ndirty" */
int dummy2; /* old "nrefill" */
int dummy3; /* unused */
int interval; /* jiffies delay between kupdate flushes */
int age_buffer; /* Time for normal buffer to age before we flush it */
int nfract_sync;/* 脏块比例达到多少唤醒bdflush进程进行同步flush */
int dummy4; /* unused */
int dummy5; /* unused */
} b_un;
unsigned int data[N_PARAM];
} bdf_prm = {{40, 0, 0, 0, 5*HZ, 30*HZ, 60, 0, 0}}; //默认值是40%和60%
(1)struct buffer_head * getblk(kdev_t dev, int block, int size)
for (;;) { //循环一直到申请成功
struct buffer_head * bh;
bh = get_hash_table(dev, block, size); //从hash表中得到一个bh
if (bh)
return bh; //如果可以的话就返回
if (!grow_buffers(dev, block, size)) //如果
free_more_memory(); //释放一部分buffers
}
(2)static int grow_buffers(kdev_t dev, unsigned long block, int size)
/* size大小必须是设备硬扇区大小的整数倍 */
if (size & (get_hardsect_size(dev)-1))
BUG();
/* Size大小必须在512字节和PAGE_SIZE之间 */
if (size < 512 || size > PAGE_SIZE)
BUG();
//将块的序号进行页对齐,例如页大小为4K,块设备的大小为1K,那么第5块的index就是
//2,这里的index就是以4K页来计算的号码
sizebits = -1;
do {
sizebits++;
} while ((size << sizebits) < PAGE_SIZE);
index = block >> sizebits;
block = index << sizebits;
page = grow_dev_page(bdev, index, size); //产生一页
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -