📄 buffer.c.txt
字号:
hash_page_buffers(page, dev, block, size); //将这个新产生的页加入到相应的hash表中
(3)static struct page * grow_dev_page(struct block_device *bdev, unsigned long index, int size)
page = find_or_create_page(bdev->bd_inode->i_mapping, index, GFP_NOFS); //首先找到一个页面
bh = page->buffers; //先得到这个页面的buffer域
if (bh) { //如果buffers域中有数据
if (bh->b_size == size) //如果正好是这个大小的,就返回这个页面
return page;
if (!try_to_free_buffers(page, GFP_NOFS)) //否则,试着释放掉这个页面
goto failed;
}
bh = create_buffers(page, size, 0); //否则的话就在这个页面上产生一个新的buffer
if (!bh)
goto failed;
link_dev_buffers(page, bh);
return page;
(4)static struct buffer_head * create_buffers(struct page * page, unsigned long size, int async)
struct buffer_head *bh, *head;
long offset;
head = NULL;
offset = PAGE_SIZE;
while ((offset -= size) >= 0) { //把这一页都设置为size大小的buffer
bh = get_unused_buffer_head(async); //必须首先得到一个有效的buffer_head结构
if (!bh)
goto no_grow; //如果没有,就释放一些buffer_head结构
bh->b_dev = NODEV;
bh->b_this_page = head; //设置在一个页中的buffer环
head = bh;
…… //对bh进行初始化
set_bh_page(bh, page, offset);
bh->b_list = BUF_CLEAN;
bh->b_end_io = NULL;
}
return head;
void set_bh_page (struct buffer_head *bh, struct page *page, unsigned long offset)
{
if (offset >= PAGE_SIZE) //进行合法性检查
BUG();
bh->b_data = page_address(page) + offset; //数据的开始地址,结束地址可以使用开始
//地址加上bh->b_size来进行计算
bh->b_page = page; //纪录这个buffer所处的内存页
}
(4)static inline void link_dev_buffers(struct page * page, struct buffer_head *head)
struct buffer_head *bh, *tail;
bh = head;
do {
tail = bh;
bh = bh->b_this_page; //寻找这个buffer所在的内存页面的所有的buffer,
} while (bh);
tail->b_this_page = head; //把属于一个内存页面的所有的buffer连接成为一个环
page->buffers = head;
page_cache_get(page);
()static void free_more_memory(void)
balance_dirty(); //计算脏块所占的比例
wakeup_bdflush(); //唤醒写脏块进程
try_to_free_pages(GFP_NOFS);
run_task_queue(&tq_disk);
__set_current_state(TASK_RUNNING);
sys_sched_yield();
(3)void balance_dirty(void)
int state = balance_dirty_state(); //计算flush策略
if (state < 0) //如果脏块比例不打,返回
return;
wakeup_bdflush(); //唤醒flush进程
if (state > 0) { //如果是同步flush的话,就等待他完成
spin_lock(&lru_list_lock);
write_some_buffers(NODEV);
wait_for_some_buffers(NODEV);
}
//返回值-1表示不用flush
//0-->异步flush
//1-->同步flush
(4)static int balance_dirty_state(void)
…… //计算脏的比例
soft_dirty_limit = tot * bdf_prm.b_un.nfract;
hard_dirty_limit = tot * bdf_prm.b_un.nfract_sync;
//如果脏块数量占的比例不是很大,那么就异步flush,也就是仅仅唤醒flush进程
if (dirty > soft_dirty_limit) {
if (dirty > hard_dirty_limit && !(current->flags & PF_NOIO))
return 1; //如果脏块比例过大,那么就唤醒flush进程并等待他完成
return 0;
}
*********************************************************************
***********************异步等待操作**********************************
相关文件
include/linux/locks.h
这个函数主要是运行块设备的运行例程,从而将传入的buffer_head上所制定的操作完成
void __wait_on_buffer(struct buffer_head * bh)
DECLARE_WAITQUEUE(wait, tsk); //生命等待队列
get_bh(bh); //buffer_head计数加一
add_wait_queue(&bh->b_wait, &wait); //把buffer_head加入到等待队列中
do {
run_task_queue(&tq_disk); //运行磁盘操作队列,进行读写
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
if (!buffer_locked(bh)) //如果bh还是加锁,说明没有完成操作
break;
schedule(); //调度,等待
} while (buffer_locked(bh)); //直到bh解锁
…… //后续处理
*********************************************************************
**************************异步读操作**********************************
struct buffer_head * bread(kdev_t dev, int block, int size)
bh = getblk(dev, block, size); //的到一个buffer_head结构
touch_buffer(bh);
if (buffer_uptodate(bh)) //如果的缓存结构中的buffer_head已经是更新的了,就直接返回
return bh;
ll_rw_block(READ, 1, &bh); //否则进行读操作
wait_on_buffer(bh); //等待读操作完成
if (buffer_uptodate(bh))
return bh;
brelse(bh); //释放bh
return NULL; //失败的读操作
**********************************************************************
***************************direct I/O操作*****************************
(1)
int brw_kiovec(int rw, int nr, struct kiobuf *iovec[],
kdev_t dev, unsigned long b[], int size)
for (i = 0; i < nr; i++) { //对于每一个输入的kiobuf的结构
iobuf = iovec[i];
…… //都要进行合法性检查
}
bufind = bhind = transferred = err = 0;
for (i = 0; i < nr; i++) { //可以walk这些vec来进行读写了
iobuf = iovec[i];
offset = iobuf->offset; //
length = iobuf->length; //取出这个kiobuf要读写的数据长度
if (!bhs)
bhs = iobuf->bh; //取出这个kiobuf的bh池
for (pageind = 0; pageind < iobuf->nr_pages; pageind++) { //遍历kiobuf中所有的页
map = iobuf->maplist[pageind]; //得到一个页面结构
while (length > 0) { //如果读写的长度不是0
blocknr = b[bufind]; //挨个查询块号
if (blocknr == -1UL) { //如果块号是负数
if (rw == READ) {
memset(kmap(map) + offset, 0, size);
flush_dcache_page(map);
kunmap(map);
transferred += size;
goto skip_block;
} else
BUG();
} //????
if (iobuf->dovary && (offset == 0)) {
iosize = RAWIO_BLOCKSIZE;
if (iosize > length)
iosize = length;
} //????
bufind += (iosize/size); //得到下一个读写的这个设备的块号。
tmp = bhs[bhind++]; //从kiobuf的bh池中取出一个bh结构
tmp->b_size = iosize; //设置bh的读写大小
set_bh_page(tmp, map, offset); //将取到的页面挂在bh上
tmp->b_this_page = tmp; //
init_buffer(tmp, end_buffer_io_kiobuf, iobuf); //设置回调函数
tmp->b_dev = dev; //设置读写设备号
tmp->b_blocknr = blocknr; //设置读写的块号
…… //其它设置
/*
* 这行代码很重要。使用这个io_count来对这个kiobuf
* 中所提交的bh进行计数跟踪。
*/
atomic_inc(&iobuf->io_count);
submit_bh(rw, tmp); //进行实际读写
/*
* 如果kiobuf中的bh池(kiobuf->bh)中已经没有bh了,就等待
* 先前做的kiobuf的读写完成,释放一部分bh。
*/
if (bhind >= KIO_MAX_SECTORS) {
kiobuf_wait_for_io(iobuf); /* wake-one */
err = wait_kio(rw, bhind, bhs, size);
if (err >= 0)
transferred += err;
else
goto finished;
bhind = 0; //释放原来的bh结构,供后续操作使用
}
length -= iosize; //从要读取的总长度中减去已经做过的
offset += iosize; //偏移量也增加相应的长度
if (offset >= PAGE_SIZE) { //如果偏移量已经超出了一个页面
offset = 0; //偏移量置为0
break; //推出这个页面的循环,取新页面。
}
}
}
}
if (bhind) { //如果bhind不为0,说明还有剩余的bh没有完成
kiobuf_wait_for_io(iobuf);
err = wait_kio(rw, bhind, bhs, size); //等待这些bh的完成
if (err >= 0)
transferred += err;
else
goto finished;
}
(2)
static void end_buffer_io_kiobuf(struct buffer_head *bh, int uptodate)
mark_buffer_uptodate(bh, uptodate); //标志数据块已经更新
kiobuf = bh->b_private; //取出kiobuf结构
end_kio_request(kiobuf, uptodate); //完成kiobuf请求
unlock_buffer(bh); //解锁bh。
(3)
/*
* 这个函数很简单,就是对于参数指定数目nr的bh结构数据,遍历这些bh结构,等待,
* 直到所有的bh都更新了。
*/
static int wait_kio(int rw, int nr, struct buffer_head *bh[], int size)
**********************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -