http://blog.csdn.net/faithsws/article/details/5381424
前天晚上在写完另一篇总结之时,赵XX向我咨询了关于mtd 和ubifs的相关内容。而我在这方面只是略懂皮毛,所以向他许愿共同调查这个方面的知识。
经过昨天一天的调查,最后感觉是有了一定的经验和基础了,所以要赶紧记录下来。
不知道按什么顺序来讲述这个方面的内容,那么还是按照我的疑问过程来一步步解析吧。
Mtd
Mtd的意思是Memory technology device,就是存储技术设备的意思,多指flash。但是这个概念在最终解析到内核源码是,有了一定的改变。
Ubifs
Ubifs 是一个新兴的应用于mtd上的有效的文件系统。可以有效的组织flash的坏块和peb的负载平衡,同时提供访问速度,减小内存消耗,具有日志的功能。是JFFS2的后续增强版。
三.疑问
初识ubifs,相信大家都是从uibfs的操作工具来开始的。通过ubifs的工具可以attach/detach mtd,create/destroy ubi_volume, resize/rename ubi_volume, read/write ubi_volume。在操作的过程中,我的第一个疑问是:
在mtd设备上存在着partition,在ubi上存在volume,他们之间什么关系,同时也存在着两个概念mtd device,ubi device,他们之间的区别和联系又是什么?
对这个问题的解答,我并没有从网络获得明确的答案。所以我转到对内核的解析上。对于第二个提问,我在内核(2.6.29)中的ubifs.txt上,有了一个大致概念。先说这个,mtd device 代表着物理设备,这个物理设备上存在着许多物理块(nand),这些块大部分是好的,也有少数是坏块,所以mtd device 代表着所有的好块和坏块。对这些mtd device,内核提供了读写等操作。
Ubi device 代表着物理介质上的逻辑设备。跟mtd device一样,也有读写等操作接口。可是这个设备在用户看来是没有坏块的,ubi device 负责了坏块的管理,并且对物理块进行了新的组织,即使在用户层看来这是一个串行的读写设备,但是在ubi device中,却是进行了新的映射。这种映射目的在于负载的均衡。
再说第一个提问。首先是对mtd 部分的解析。以下是结果。
Mtd 在初始化时,会将同一类型的flash划分成一个mtd device。这个device的大小等于所以芯片容量的总和。这个过程完成后,就会从内核启动参数或者默认的分区表中获得分区信息。最后,将每个分区作为,一个mtd device添加到mtd_tables中。
我们在cat /proc/mtd时,就是返回的mtd_table中的信息。
下图指示了整个流程。
这里有一点重要的是:在parse_mtd_partition的cmdline中一个关键点是需要mtd->name = mtd->id,否则parse将返回错误结果。这是在开发驱动的时候,应该了解的。
另外一个重要结构是:mtd_table。其中保存了所有的mtd device的信息。
至此我们知道了mtd 上的partition 最终被作为mtd device保存到了mtd_table中了。
继续,ubifs的部分。
这个部分我只能写一下流程了,画图更能说明问题。
从中我们可以看出,最后每个ubi volume又被模拟成partition 添加到了mtd_table中了。
因此可以得到一个总体的mtd 和ubi 的关系图。
每个mtd partition 可以attach 到一个ubi device上,在每个ubi device上又可以创建很多ubi volume,而每个ubi volume又被作为一个mtd device 保存于mtd table 中。
从内核中我们可以看到mtd的type分为 nor ,nand,ram,rom,ubivolume。
下面是一个详细的关于用户空间设备节点的流程图。
好了,到这里,大致的概念已经清楚了。进而产生了第二个问题。
关于ubifs的内核启动参数的意义都是什么?
下面是一个uboot中的内核启动参数。
Bootargs=console=ttyS0,115200n8 ubi.mtd=4 root=ubi0:rtfs rootfstype=ubifs rw mtdparts=café_nand:200M(part1),300M(part2),400M(part3),500M(part4),-(part5)
其中console部分是定义关于串口的参数,这里不解释了。
ubi.mtd=4 指示整个系统的根文件系统在第四个mtd 上,系统将据此默认把mtd4 attach到ubi0 上。
root=ubi0:rtfs 指示 根文件系统在ubi0上的名字叫做rtfs的volume上。注意这里ubifs的设备名字的写法,不是以/dev开头。他的写法有两种:
a) 【ubi device名b) 】:【ubi volume名c) 】,d) 例如ubi0:rootfs
e) 【ubi device名f) 】_【ubi volume 编号】,g) 例如ubi0_0
rootfstype=ubifs 指示rootfs的文件系统类型为ubifs
mtdparts=café_nand:200M(part1),300M(part2),400M(part3),500M(rootfs),-(part5)
定义了物理分区表。格式为
Mtd_id:[-]size[@offset](name)[mask_flag], …,…
其中[]中的代表可有可无项。
Mtd_id 对应于某种类型flash init过程中的name,二者必须相同,才能进行有效分区。
- 表示该分区划分所有的剩余空间。
Size 指示当前分区的大小。
@引导offset的开始。
Offset 指示该分区的起始偏移量。
()中表示该物理分区(partition)的名字。
Name 表示该物理分区(partition)的名字.
Mask_flag 表示该分区的读写属性。
, 指示还有其他分区。
那么现在回头去看例子中的参数含义就明了了。
系统中存在五个partition,在第四个partition上安放着rootfs,而具体的,rootfs存在于第四个partition 对应的ubi0的名字叫做rtfs的volume上。
四.补充
1. 在add_mtd_device中,有一个noitifier的钩子操作。这样只要添加了一个mtd device,我们就可通过notifier 获知并进行相关的操作。实际上,内核也是利用这个钩子,在每当创建mtd device 时,通过udev 在用户态的/dev/下,为其创建设备节点。这个功能或许在以后编写驱动时能够满足某种特殊需求。
2. 在parse_mtd_partition 之处的指点。通常parse_mtd_partition 会跟据 parse 的type 来进行分析,
如果parse_mtd_partition 的返回大于0,说明操作成功了,获得了分区表。而如果返回小于等于0,说明操作失败,没有分区表。这个时候就可以将预先定义好的分区表作为参数传递给add_mtd_partition。这样就实现了用户预分配分区(固定分区)的功能。
3.始终在内核之中没有找到关于“ubi.mtd=?”的代码,所以对该部分的解释来源于网络,正确与否也依赖于此。
4.当ubi作为模块加载时,一个参数名叫做mtd。此时mtd=?的作用就是将mtd指定的分区attach到最后一个没被使用的ubi device的索引上。
5. ubifs的resize 代表着保留着的用于坏块处理的空间可以扩充最多2%的整体空间。详细需要参考ubifs的开发文档。
6. ubifs 跟随他的物理分区,一旦被烧写大小就固定下来。除非重新进行分区,重新烧写。
7. 不明白为什么现在所做的实验:ubiattach /dev/ubi_ctrl –m 1 会失败? 不负责任的解释:内核版本2.6.26并不是ubifs的稳定版(2.6.30)。
相信后续会有很多问题,但目前先止步于此,毕竟需求是向前的动力。
在调查的过程中,做了许多的实验,所以这篇总结的底气还是很足的,因为都是实验的结果。通过现象看内核,再从内核预测现象,这样反复,就成就了印象深刻的结论。
跟赵慧鹏的交流也很快乐,大家都是初学者,同样提出不一样的问题,从而促进解析的深入进行。从问题的一点进入,在慢慢扩散,慢慢渗透,不断从高的层面总结,是这次调查的收获。