在linux中,mtd是指“内存技术设备”,是存储设备中的一个子系统。linux引入MTD系统是为了给NOR FLASH和NAND FLASH设备提供统一接口。MTD设备通常可分为四层:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。
本教程操作环境:linux5.9.8系统、Dell G3电脑。
Linux MTD是什么?
MTD全称“Memory Technology Device”,意思为“内存技术设备”,是Linux的存储设备中的一个子系统。
在Linux内核中,引入MTD层为NOR FLASH和NAND FLASH设备提供统一接口。MTD将文件系统与底层FLASH存储器进行了隔离。
设计此MTD系统的目的是,对于内存类的设备,提供一个抽象层,一个接口,使得对于硬件驱动设计者来说,只需要去提供最简单的底层硬件设备的读/写/擦除函数就可以了,数据对于上层使用者来说是如何表示的,可以不关心,因为MTD存储设备子系统都帮你做好了。
MTD框架
Linux的MTD设备位于drivers/mtd/下面。
MTD文件下的内容如下:
MTD设备通常可分为四层
上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。
1.cmdlinepart.c
当mtd分区表由u-boot通过cmd参数传输给linux时,linux内核可以不用对mtdparts进行注册添加,只需要将MTD中的command line partition选项开启即可。使用这种的方法u-boot下需要对MTD进行支持,且所传输的mtd分区参数要符合格式要求。
2.devices文件夹
当我们有一个spi flash设备时且要使用mtd进行管理,我们一般会将其放在devices文件夹下,如devices文件夹下面的m25p80.c就是一个典型的spi flash设备。
3.chips/nand/onenand文件夹
nand flash 驱动在nand文件夹下;
onenand flash 驱动在onenand文件夹下;
nor flash比较杂,下面几个文件下都会有:
chips:cfi/jedec接口通用驱动
devices:nor flash底层驱动(spi flash)
maps:nor flash映射关系相关函数
4.核心文件
mtdchar.c : MTD字符设备接口相关实现,设备号31;
mtdblock.c : MTD块设备接口相关实现,设备号90,;
mtdcore.c: MTD原始设备接口相关实现;
mtdpart.c : MTD分区接口相关实现。
5.ubi
ubifs文件的支持层,当使用ubifs文件系统时,需要将Device Drivers -> Memory Technology Device (MTD) support -> UBI -Unsorted block image 中的Enable UBI选中。
将File systems -> Miscellaneous filesystems中的UBIFS file system support选中。
MTD分区表的实现
在开机过程从console经常可以看到类似以下信息,
0x000000000000-0x000000100000 : "Bootloade" 0x000000100000-0x000002000000 : "Kernel" 0x000002000000-0x000003000000 : "User" 0x000003000000-0x000008000000 : "File System"
这就是MTD给我们一种最直观的表示形式,给我们展示了内存中各模块的分区结构,但这些分区是怎样实现的呢?分区表的实现方式有几种,下面进行分别说明:
注:分区表实现的前提是MTD设备驱动已经成功了,否则连驱动都没成功就无分区可说了。
1.内核中添加
在内核中添加这是一个比较经常使用的方法,随便一本驱动移植的书上应该都有,主要就是在平台设备里面添加mtd_partition,添加类似下面的信息,这边就不过多描述
struct mtd_partition s3c_nand_part[] = { { .name = "Bootloader", .offset = 0, .size = (1 * SZ_1M), .mask_flags = MTD_CAP_NANDFLASH, }, { .name = "Kernel", .offset = (1 * SZ_1M), .size = (31 * SZ_1M) , .mask_flags = MTD_CAP_NANDFLASH, }, { .name = "User", .offset = (32 * SZ_1M), .size = (16 * SZ_1M) , }, { .name = "File System", .offset = (48 * SZ_1M), .size = (96 * SZ_1M), } }; static struct s3c_nand_set s3c_nand_sets[] = { [0] = { .name = "nand", .nr_chips = 1, .nr_partitions = ARRAY_SIZE(s3c_nand_part), .partitions = ok6410_nand_part, }, }; static struct s3c_platform_nand s3c_nand_info = { .tacls = 25, .twrph0 = 55, .twrph1 = 40, .nr_sets = ARRAY_SIZE(s3c_nand_sets), .sets = ok6410_nand_sets, }; static void __init s3c_machine_init(void) { s3c_nand_set_platdata(&s3c_nand_info); }
因为我们的MTD驱动已经完成了,当device和driver匹配后会调用驱动中的probe接口函数,我们需要在probe函数里面调用add_mtd_partitions(s3c_mtd, sets->partitions, sets->nr_partitions);
实现分区表的添加。
2.u-boot传参
在u-boot下可以通过添加mtdparts信息到bootargs中,u-boot启动后会将bootargs中的信息传送给kernel,,kernel在启动的时候会解析bootargs中mtdparts的部分,这边举个例子:
mtdparts=nand.0:1M(Bootloader)ro,31M(Kernel)ro,16M(User),96M(File System)
,更具体的mtdparts格式可以查阅下相关资料。
为了使kernel能够解析mtdparts信息,我们需要将内核中的Device Drivers -> Memory Technology Device (MTD) support ->Command line partition table parsing选项开启,这在上面已经说过。
在内核中添加分区表的时候,我们是在平台设备里面加入mtd_partition信息。这边通过u-boot传参则取消平台设备里面的partition信息,那我们需要怎样解析u-boot的传过来的mtdparts呢。
u-boot传参过来后,cmdlinepart.c中会将这些参数解析好,存在里面LIST_HEAD(part_parsers)
链表里面,然后我们在驱动的probe函数中,通过调用mtd_device_parse_register(mtd, probe_types,&ppdata, NULL, 0);
函数。
mtd_device_parse_register()
函数位于drivers/mtd/mtdcore.c 中,内容如下:
int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, struct mtd_part_parser_data *parser_data, const struct mtd_partition *parts, int nr_parts) { int err; struct mtd_partition *real_parts; err = parse_mtd_partitions(mtd, types, &real_parts, parser_data); if (err <= 0 && nr_parts && parts) { real_parts = kmemdup(parts, sizeof(*parts) * nr_parts, GFP_KERNEL); if (!real_parts) err = -ENOMEM; else err = nr_parts; } if (err > 0) { err = add_mtd_partitions(mtd, real_parts, err); kfree(real_parts); } else if (err == 0) { err = add_mtd_device(mtd); if (err == 1) err = -ENODEV; } return err; }
可以看到该函数会先执行parse_mtd_partitions(mtd, types, &real_parts, parser_data);
函数,后面还是通过add_mtd_partitions()
函数来实现分区表的添加。
parse_mtd_partitions()
函数位于drivers/mtd/mtdpart.c中,内容如下:
int parse_mtd_partitions(struct mtd_info *master, const char *const *types, struct mtd_partition **pparts, struct mtd_part_parser_data *data) { struct mtd_part_parser *parser; int ret = 0; if (!types) types = default_mtd_part_types; for ( ; ret <= 0 && *types; types++) { parser = get_partition_parser(*types); if (!parser && !request_module("%s", *types)) parser = get_partition_parser(*types); if (!parser) continue; ret = (*parser->parse_fn)(master, pparts, data); put_partition_parser(parser); if (ret > 0) { printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", ret, parser->name, master->name); break; } } return ret; }
进入parse_mtd_partitions()
函数会先判断types的类型,如果为空则给默认值,types的类型一般就两种,如下:
static const char * const default_mtd_part_types[] = { "cmdlinepart", "ofpart", NULL };
第一个"cmdlinepart"即u-boot传参的方式,第二个"ofpart"即下面要讲到的使用dts传参的方式,判断完类型后,就通过get_partition_parser
去解析part_parsers
链表里面的数据,这样就完成u-boot参数的解析。
3.dts传参
在Linux3.14以后的linux版本中,加入一个新的知识DTS(Device tree),dts其实就是为了解决ARM Linux中的冗余代码,在Linux2.6版本的arch/arm/plat.xxx和arch/arm/mach.xxx中充斥着大量的垃圾代码,采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码,关于dts可以自行查阅资料。
dts传参的原理其实和u-boot一样,区别在于:u-boot的时候是通过cmdlinepart.c文件实现分区信息写入LIST_HEAD(part_parsers)
链表,dts则是用过ofpart.c文件实现分区信息写入LIST_HEAD(part_parsers)
链表,所以同样要把ofpart.c文件的宏打开,在调用mtd_device_parse_register(mtd, probe_types,&ppdata, NULL, 0);
函数的时候types要设置成ofpart。
如果去对比Linux2.6版本和Linux3.14版本,会发现drivers/mtd/ofpart.c和drivers/mtd/mtdpart.c文件有所不同,Linux3.8版本里面多了Device tree这一部分的内容,感兴趣的可以自己深究下。
这边举个dts的例子:
pinctrl-0 = <&s3c_nand_flash>; ranges = <0 0 0x000000000000 0x000008000000>; /* CS0: NAND */ nand@0,0 { partition@1 { label = "Bootloader"; reg = <0x000000000000 0x000000100000>; }; partition@2 { label = "Kernel"; reg = <0x000000100000 0x000002000000>; }; partition@3 { label = "User"; reg = <0x000002000000 0x000003000000>; }; partition@4 { label = "File System"; reg = <0x000003000000 0x000008000000>; }; };
Linux mtd system的分析就到这边,有感悟时会持续会更新。
相关推荐:《Linux视频教程》
以上是linux mtd是什么的详细内容。更多信息请关注PHP中文网其他相关文章!

Linux网络配置可以通过以下步骤完成:1.配置网络接口,使用ip命令临时设置或编辑配置文件持久化设置。2.设置静态IP,适合需要固定IP的设备。3.管理防火墙,使用iptables或firewalld工具来控制网络流量。

维护模式在Linux系统管理中扮演关键角色,帮助进行系统修复、升级和配置变更。1.进入维护模式可以通过GRUB菜单选择或使用命令“sudosystemctlisolaterescue.target”。2.在维护模式下,可以执行文件系统修复和系统更新等操作。3.高级用法包括重置root密码等任务。4.常见错误如无法进入维护模式或挂载文件系统,可通过检查GRUB配置和使用fsck命令修复。

使用Linux维护模式的时机和原因:1)系统启动问题时,2)进行重大系统更新或升级时,3)执行文件系统维护时。维护模式提供安全、控制的环境,确保操作的安全性和效率,减少对用户的影响,并增强系统的安全性。

Linux中不可或缺的命令包括:1.ls:列出目录内容;2.cd:改变工作目录;3.mkdir:创建新目录;4.rm:删除文件或目录;5.cp:复制文件或目录;6.mv:移动或重命名文件或目录。这些命令通过与内核交互执行操作,帮助用户高效管理文件和系统。

在Linux中,文件和目录管理使用ls、cd、mkdir、rm、cp、mv命令,权限管理使用chmod、chown、chgrp命令。1.文件和目录管理命令如ls-l列出详细信息,mkdir-p递归创建目录。2.权限管理命令如chmod755file设置文件权限,chownuserfile改变文件所有者,chgrpgroupfile改变文件所属组。这些命令基于文件系统结构和用户、组系统,通过系统调用和元数据实现操作和控制。

MaintenancemodeInuxisAspecialBootenvironmentforforcalsystemmaintenancetasks.itallowsadMinistratorStoperFormTaskSlikerSettingPassingPassingPasswords,RepairingFilesystems,andRecoveringFrombootFailuresFailuresFailuresInamInimAlenimalenimalenrenmentrent.ToEnterMainterMainterMaintErmaintErmaintEncemememodeBoode,Interlecttheboo

Linux的核心组件包括内核、文件系统、Shell、用户空间与内核空间、设备驱动程序以及性能优化和最佳实践。1)内核是系统的核心,管理硬件、内存和进程。2)文件系统组织数据,支持多种类型如ext4、Btrfs和XFS。3)Shell是用户与系统交互的命令中心,支持脚本编写。4)用户空间与内核空间分离,确保系统稳定性。5)设备驱动程序连接硬件与操作系统。6)性能优化包括调整系统配置和遵循最佳实践。

Linux系统的五个基本组件是:1.内核,2.系统库,3.系统实用程序,4.图形用户界面,5.应用程序。内核管理硬件资源,系统库提供预编译函数,系统实用程序用于系统管理,GUI提供可视化交互,应用程序利用这些组件实现功能。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

记事本++7.3.1
好用且免费的代码编辑器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器
将Eclipse与SAP NetWeaver应用服务器集成。

SublimeText3汉化版
中文版,非常好用

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能