Chinaunix首页 | 论坛 | 博客
  • 博客访问: 752163
  • 博文数量: 79
  • 博客积分: 2671
  • 博客等级: 少校
  • 技术积分: 1247
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-02 15:26
个人简介

宅男

文章分类

全部博文(79)

文章存档

2017年(11)

2016年(12)

2015年(6)

2012年(10)

2011年(33)

2010年(7)

分类: LINUX

2011-04-25 16:30:49

我们知道在每个nandflash的驱动中,都会在nand_scan函数之后调用add_mtd_partitionsnandflash设备虚拟为若干个逻辑块设备。也就是我们一般看到的mtdblock

下面我们看一下这些函数的调用:

add_mtd_partitions

->add_mtd_device:

list_for_each(this, &mtd_notifiers) {

struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);

not->add(mtd);

}

add_mtd_device中会遍历的调用通知链mtd_notifiers中的所有注册函数。下面我们看看到底注册了哪些函数。

(1)init_mtdchar函数中调用register_mtd_user(¬ifier);注册

该函数对于的是mtd_notify_add函数,主要为每一个分区建立一个/dev/mtdX./dev/mtdXro的接口。

(2)register_mtd_blktrans函数中注册register_mtd_user(&blktrans_notifier);

static void blktrans_notify_add(struct mtd_info *mtd)

{

struct list_head *this;

if (mtd->type == MTD_ABSENT)

return;

list_for_each(this, &blktrans_majors) {

struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);

tr->add_mtd(tr, mtd);

}

}

其中tr->add_mtd用于为不同的ftl转换层注册接口。

我们分别考察下面的几个add_mtd

(1)mtdblock_add_mtd

(2)ftl_add_mtd

其中ftl_add_ mtd用于扫描一个分区来驱动是否是满足ftl的一个fat文件系统。

mtdblock_add_mtd函数用于为每一个分区建立mtdblock接口。

此时我们假设该分区是经过flasheraseall工具擦除过的一个分区。那么显然不是一个有效地fat文件系统。注册之后的各个结构体之间的关系如下图所示。

图一:blktrans_dev注册过程中各个结构体之间的关系

不同的接口也就意味着不同的处理方法,比如通过mtdblock接口进行操作的话(如mount -t vfat  /dev/mtdblock2 /tmp 或者 cp /test.txt /dev/mtdblock2等等),那么显然它最终都是调用的都是mtdblock_tr中的处理办法。

static struct mtd_blktrans_ops mtdblock_tr = {

.name = "mtdblock",

.major = 31,

.part_bits = 0,

.open = mtdblock_open,

.flush = mtdblock_flush,

.release = mtdblock_release,

.readsect = mtdblock_readsect,

.writesect = mtdblock_writesect,

.add_mtd = mtdblock_add_mtd,

.remove_dev = mtdblock_remove_dev,

.owner = THIS_MODULE,

};

假如我现在设计了一种新型的ftl方案,其struct mtd_blktrans_ops如下:

static struct mtd_blktrans_ops  md_tr = {

.name = "md",

.major = 35,

.part_bits = 0,

.open = md_open,

.flush = md_flush,

.release = md_release,

.readsect = md_readsect,

.writesect = md_writesect,

.add_mtd = md_add_mtd,

.remove_dev = md_remove_dev,

.owner = THIS_MODULE,

};

那么在通过/dev/mdX接口的所有操作都会最终追溯到上面的md_tr中的函数指针。

下面来分析一下fat文件系统的读写流程:

我们知道fat文件系统是为block类设备而设计的一种文件系统。

static int fat_writepage(struct page *page, struct writeback_control *wbc)

{

return block_write_full_page(page, fat_get_block, wbc);

}

block_write_full_page最终会调用到submit_bh来进行将buffer-head中数据写入到block设备中去。

Submit_bh

->submit_bio

->generic_make_request

该函数会调用到相应队列的make_request_fn函数指针。这个函数指针在初始化队列的时候会重定向,比如在注册mtdblock接口的时候调用register_mtd_blktrans函数:

tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);

函数blk_init_queue的输入参数中有一个是函数,该函数用于对队列的make_request_fn函数指针进行重定向。

最终每一个具体的io请求都挂到相应的gendiskrequest_queue中去了。这样一次io请求就算发出去了。

既然io请求发出了,那么这样的请求是怎么样具体的落实下去的呢?

同样在register_mtd_blktrans函数中:

ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL);

 kernel_thread创建了一个进程。这样我就明白了。内核通过一个后台进程来处理这样具体的io请求。

mtd_blktrans_thread

->do_blktrans_request:

其中do_blktrans_request十分明了,根据request命令是读还是写来具体的调用struct mtd_blktrans_opswritesect还是readsect

阅读(5159) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~