linux使用RAM_DISK根文件系统基本过程
linux使用RAM_DISK根文件系统基本过程
以下只是讲述了内存文件系统从uboot到内核、最终挂载到根目录的一个基本流程,至于每个环节的细节,尚未深究,有兴趣的可以再深入学习一下。
一、 内存文件系统
1、 分类
ramdisk、ramfs、tmpfs
2、 ramdik
先格式化(格式化成相应的文件系统)再挂载,大小固定,之后不能随便更改。基于虚拟在内存中的其他文件系统,例如:
mke2fs/dev/ram0; mount /dev/ram0 /mnt/mtd
3、 ramfs
内存文件系统,处于虚拟文件系统(VFS)层,无需格式化,可以创建多个,只要内存足够,在创建时可以执行最大能使用的内存大小,针对物理内存。例如:
mount –tramfs /dev/mem /mnt/mtd or
mount –tramfs none /mnt/mtd –o maxsize=2000(kbyte为单位)
4、 tmpfs
虚拟内存文件系统,可以使用物理内存,也可以使用交换分区
二、 基本原理
Flash的mtdblock1存放根文件系统的相关文件,包括uImage、cramfs.initrd.img等。uboot启动后会将uImage和cramfs.initrd.img加载到内存的相应地址,然后uboot引导内核启动。内核启动根据cramfs.initrd.img存放地址将其挂载到根目录下,因此我们进入shell所看见的根目录就是对应的内存文件系统内容。挂载成功之后,就可以将mtdblock1的其他目录文件挂载到内存文件系统下,进行文件方式访问、操作等。
三、 Uboot阶段加载initrd.img
1、加载uImage和initrd.img文件到系统内存
在加载uImage的地方添加加载内存根文件系统cramfs.initrd.img文件,两个文件的地址不能重叠,另外不要存放在内核启动的地址0x80008000。这里分别存放在0x82000000和0x81000000的地方,代码如下:
int do_fsload(cmd_tbl_t*cmdtp, int flag, int argc, char *argv[])
{
…
size = squashfs_fload((char *) offset, part,filename); ^M
#ifdefCONFIG_HI3531_V100
size = squashfs_fload((char *)CONFIG_INITRDRAMFS_LOAD_ADDR, part, "/boot/cramfs.initrd.img");
#endif
…
}
2、修改bootargs为内存根文件系统
#define BOOTARGS_3531_V100 "mem=245M,console=ttyAMA0,115200 rootfstype=squashfs " "root=/dev/ram"
3、启动内核bootm
启动内核使用如下命令:bootm 0x82000000 0x81000000。跟随两个参数,第一个为uImage加载的起始地址,第二个参数为cramfs.initrd.img的起始地址,两个参数顺序不能反,这个bootm命令实现决定,具体分析见后面。
(1)启动内核do_bootm源码解析
intdo_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
….
if (bootm_start(cmdtp,flag, argc, argv)) //获取uImage和initrd.img的镜像文件头
return 1;
…
ret = bootm_load_os(images.os,&load_end, 1); //解压uImage,此处不讲解
…
boot_fn = boot_os[images.os.os];= do_bootm_linux //调用内核最终启动函数,见后面讲解
….
boot_fn(0, argc, argv, &images); //此处成功的情况下不会执行reset
do_reset (cmdtp, flag, argc, argv);
}
(2)获取镜像头bootm_start源码解析
staticint bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
memset ((void *)&images, 0, sizeof(images)); //对images头文件进行初始化
…
os_hdr = boot_get_kernel (cmdtp, flag,argc, argv,&images, &images.os.image_start, &images.os.image_len); //获取内核镜像头,主要是获取起始地址和内核数据长度
….
switch(genimg_get_format (os_hdr)) { //获取image的相关信息
case IMAGE_FORMAT_LEGACY:
images.os.type= image_get_type (os_hdr);
images.os.comp = image_get_comp(os_hdr);
images.os.os = image_get_os (os_hdr);
images.os.end = image_get_image_end(os_hdr);
images.os.load = image_get_load(os_hdr);
break;
…
ret = boot_get_ramdisk (argc, argv, &images,IH_INITRD_ARCH, &images.rd_start, &images.rd_end); //获取initrd的起始结束地址
….
}
(3)启动内核do_bootm_linux
将initrd的相关参数加入内核参数列表传递给内核。
int do_bootm_linux(intflag, int argc, char *argv[], bootm_headers_t *images)
{
…
#ifdefined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG)
setup_start_tag (bd); //初始化boot参数bd->bi_boot_params;
…
#ifdefCONFIG_INITRD_TAG //将initrd的相关参数加入参数列表,具体实现见后面
if (images->rd_start &&images->rd_end)
setup_initrd_tag(bd, images->rd_start, images->rd_end);
#endif
…
setup_end_tag (bd);
#endif
…
theKernel (0, machid,bd->bi_boot_params); //进入kernel阶段
}
static void setup_initrd_tag (bd_t *bd, ulong initrd_start, ulonginitrd_end)
{
params->hdr.tag = ATAG_INITRD2; //内核使用initrd2相关函数解析该tag,见内核阶段
params->hdr.size = tag_size(tag_initrd);
params->u.initrd.start = initrd_start;//initrd的起始地址
params->u.initrd.size = initrd_end -initrd_start; //initrd的长度
params = tag_next (params);
}
4、kernel各个镜像地址(hi3536为例)
Images.os的各个信息:
start:0x42000000
end:0x42252000
image_start:0x42000040
image_len:0x251fc0,
load:0x40008000
load_end= load + image_len
comp:0x0: IH_COMP_NONE
type:0x2 :IH_TYPE_KERNEL
os:0x5 :IH_OS_LINUX
imagesep:0x40008000
rd_start:0x41000040
rd_end:0x4115c040
*p_virtaddr++= 0xe51ff004; /* ldr pc, [pc, #-4] */ 该地址为功能PC对应的指令代码
*p_virtaddr++= jump_addr; /* pc jump phy address */
四、 Kernel阶段处理initrd.img
1、获取initrd.img的相关参数
进入内核后,获取uboot传递过来的initrd.img参数的调用流程如下:
start_kernel()—> setup_arch() —> setup_machine_tags(),该函数会对uboot传递进来的所有tags进行解析:
if(tags->hdr.tag == ATAG_CORE)
{
if (meminfo.nr_banks != 0)
squash_mem_tags(tags);
save_atags(tags);
parse_tags(tags);
}
其中函数parse_tag_initrd2就是解析内存文件系统的参数:获取内存文件系统的内存地址和长度,这个可以和前面的uboot对应起来:
staticint __init parse_tag_initrd2(const struct tag *tag)
{
//dump_stack();
phys_initrd_start = tag->u.initrd.start;
phys_initrd_size = tag->u.initrd.size;
return 0;
}
2、内存文件系统占用的内存处理
调用流程如下:start_kernel()—> setup_arch() —> arm_memblock_init(),该函数会对上面获取到的内存文件系统占用内存进行处理:
#ifdefCONFIG_BLK_DEV_INITRD
if (phys_initrd_size &&!memblock_is_region_memory(phys_initrd_start,phys_initrd_size)) {
phys_initrd_start = phys_initrd_size =0;
}
if (phys_initrd_size &&memblock_is_region_reserved(phys_initrd_start,phys_initrd_size)) {
phys_initrd_start = phys_initrd_size =0;
}
if (phys_initrd_size) {
memblock_reserve(phys_initrd_start,phys_initrd_size);
/* Now convert initrd to virtual addresses*/
initrd_start= __phys_to_virt(phys_initrd_start);
initrd_end= initrd_start + phys_initrd_size;
}
#endif
其中initrd_start和initrd_end为ramfs占用的虚拟内存起始和结束地址。
3、各种init调用顺序
在讲内存文件系统挂载之前,先讲一下各种initcall。arch/arm/kernel/vmlinux.lds文件规定了各个initcall的存放段:
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
__pv_table_begin = .;
*(.pv_table)
__pv_table_end = .;
. = ALIGN(16); __setup_start = .;*(.init.setup) __setup_end = .;
__initcall_start= .; *(.initcallearly.init) __early_initcall_end= .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init)*(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init)*(.initcall3s.init) *(.initcall4.init) * (.initcall4s.init) *(.initcall5.init)*(.initcall5s.init) *(.initcallrootfs.init)*(.initcall6.init) *(.initcall6s.init) *(. initcall7.init) *(.initcall7s.init) __initcall_end = .;
__con_initcall_start = .;*(.con_initcall.init) __con_initcall_end = .;
__security_initcall_start = .; *(.security_initcall.init)__security_initcall_end = .;
. = ALIGN(4); __initramfs_start = .;*(.init.ramfs) . = ALIGN(8); *(.init.ramfs.info)
__init_begin = _stext;
*(.init.data) *(.cpuinit.data)*(.meminit.data) *(.init.rodata) *(.cpuinit.rodata) *(.meminit.rodata) . =ALIGN(32); __dtb_start = .;*(.dtb.init.rodata) __dtb_end = .;
}
上面列出了tags、setup、initcall所占的各个字段,同时说明了各个初始化顺序(即优先级),(.initcallrootfs.init)为根文件系统的初始化优先级,比较靠后。
4、内核对内存文件系统初始化
调用流程:start_kernel()—> rest_init () ,该函数创建kernel_init,该线程对上面所有注册的initcall按照顺序进行初始化。
下面对线程kernel_init相关的函数进行说明:
(1)initcallearly初始化
由函数do_pre_smp_initcalls实现,具体如下:
staticvoid __init do_pre_smp_initcalls(void)
{
initcall_t *fn;
for (fn = __initcall_start;fn < __early_initcall_end; fn++)
do_one_initcall(*fn);
}
上述蓝色地址见上面vmlinux.lds
(2)其他initcall初始化
do_basic_setup()—>do_initcalls()(do_basic_setup函数里还完成驱动等的初始化),该函数具体实现如下:
staticvoid __init do_initcalls(void)
{
initcall_t *fn;
for (fn = __early_initcall_end;fn < __initcall_end; fn++)
do_one_initcall(*fn);
}
这个函数会调用initcallroofs根文件系统的初始化函数populate_rootfs()
(3)初始化根文件系统populate_rootfs()(细节未深入)
该函数先判断initrd_start值为非0,然后将内存文件系统数据写入文件/initrd.image:
fd =sys_open((const char __user __force *) "/initrd.image",O_WRONLY|O_CREAT,0700);
if (fd>= 0) {
sys_write(fd, (char *)initrd_start,initrd_end - initrd_start);
sys_close(fd);
free_initrd();
}
5、内存文件系统挂载/目录
kernel_init()—>prepare_namespace(),该函数完成根目录挂载:
void__init prepare_namespace(void)
{
….
if (initrd_load())
goto out;
….
mount_root();
out:
devtmpfs_mount("dev");
sys_mount(".", "/",NULL, MS_MOVE, NULL);
sys_chroot((const char __user __force*)".");
}
上面代码末尾就是挂载文件系统到根目录,initrd_load()就是加载内存文件系统。
(2)initrd_load()实现
int__init initrd_load(void)
{
printk("*** mount_initrd:=%d\n",mount_initrd);
if (mount_initrd) {
create_dev("/dev/ram",Root_RAM0);
if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0){
sys_unlink("/initrd.image");
handle_initrd();
return 1;
}
}
sys_unlink("/initrd.image");
return 0;
}
将内存文件系统数据加载到/dev/ram以便后续挂在到根目录下。
五、linux使用RAM_DISK根文件系统使用方法
注下面以hi3531+3532平台为例,进行简单讲解,如有遗漏请帮忙补充
1、uboot修改
(1)bootargs修改
#defineBOOTARGS_3531_V100 "mem=135M@0x80000000 mem=135M@0xc0000000,console=ttyAMA0,115200 rootfstype=squashfs" \
"root=/dev/ram"
rootfstype以打包时使用的压缩方式为准;使用内存文件建系统,所以root不再是flash的mtdblock分区而是内存设备
(2)fsload修改
加载/boot/uImage之后,还需要将cramfs.initrd.img加载到指定的位置,注意uImage和cramfs加载空间不能重叠,且不要将cramfs加载到uImage启动地址。
if(squashfs_check(part))
{
size = squashfs_fload((char *) offset, part,filename); //加载uImage
printf("### %s loading '%s' to 0x%lx\n",fsname, "/boot/cramfs.initrd.img",\
offset-0x1000000);
size = squashfs_fload((char *)(offset -0x1000000), part, "/boot/cramfs.initrd.img"); //加载cramfs.initrd.img
}
(3)bootm修改
修改bootm—> bootm 0x82000000 0x8100000:当没有参数时bootm只解析uImage;第一个参数为内核加载的地址,第二参数为cramfs加载的地址,顺序不能反。
2、kernel修改
内核配置添加对initrd的支持:
Generalsetup --->
[*] Initial RAM filesystem and RAMdisk (initramfs/initrd) support
() Initramfs source file(s)
[*] Support initial ramdisks compressed usinggzip
[ ] Support initial ramdisks compressed usingbzip2
[*] Support initial ramdisks compressed usingLZMA
[ ] Support initial ramdisks compressed using XZ
[ ] Support initial ramdisks compressed usingLZO
DeviceDrivers --->
[*] Block devices --->
<*>RAM block devicesupport
(16) Default number of RAM disks
(4096) Default RAM disk size (kbytes)
3、打包环境修改
(1)打包目录修改
新增一个ramfs目录用于生成ramfs.initrd.img,该目录类似原来的romfs但是只保留了根文件系统所需的基本要素(节省内存),而占大内存和flash的Challenge.7z、lib.7z、usr目录等内容仍然保留在romfs目录。
调整后romfs值保留了几个目录,如下所示:
/boot:
cramfs.initrd.img uImage : 主从共用的内存文件系统、主片kernel
/slave:
u-boot.bin uImage :如果没有从片,该目录可以节省;从片的uboot/kernel
/usr:
/bin /data /etc /lib /sbin :用户所需:challenge.7z lib.7z 图片 字库等内容
(2)打包工具修改
$TOOL_DIR/mkimage-A arm -O linux -T ramdisk -a 0x0 \
-e0x83000000 -n "initrd in cramfs" -d $TMP_DIR/$TMP_FILE_RAM $TARGET_DIR/cramfs.initrd.img
cp$TARGET_DIR/cramfs.initrd.img $SRC_DIR/boot/
$TOOL_DIR/mkimage-A arm -O linux -T kernel -C gzip -a A0050000 \
-e0xA0d70000 -n 3531romfs -d $TMP_DIR/$TMP_FILE $TARGET_DIR/$TARGET_FILE
在生成romfs-x.cramfs.img前,要先生成cramfs.initrd.img,并拷贝到romfs的boot目录,然后再生成最终额romfs-x.cramfs.img。
以上就是内存文件系统使用的基本过程,实际情况有些偏差,仅供参考。
linux使用RAM_DISK根文件系统基本过程相关推荐
- linux文件系统启动流程,linux 内核启动过程以及挂载android 根文件系统的过程
转载 作者:汕头大学-黄珠唐 时间:2009 年10 月29 日 主要介绍linux 内核启动过程以及挂载android 根文件系统的过程,以及介绍android 源代码中文件系统部分的浅析. 主要源 ...
- 嵌入式Linux中的根文件系统
以下内容转载于http://mp.weixin.qq.com/s/O6MklgKekSc9gbM7zQSMEQ,有删改. 一. 首先要明白的是"什么是文件系统?",文件系统是对一个 ...
- 对嵌入式Linux中的根文件系统的理解和解析
虽然前面已经利用Busybox制作了根文件系统,但是对于根文件系统的作用和普通的文件系统之间的区别还是有点模糊.通过查找资料总结了一 下对根文件系统的理解,也欢迎大家一起讨论. 首先要明白的是& ...
- Linux内核与根文件系统的关系详解
Linux内核与根文件系统的关系 开篇题外话:对于Linux初学者来说,这是一个很纠结的问题,但这也是一个很关键的问题! 一语破天机: "尽管内核是 Linux 的核心,但文件却是用户与操作 ...
- 嵌入式Linux内核和文件系统,在IXP435上移植嵌入式Linux内核和根文件系统
简要介绍如何在IXP435上移植嵌入式Linux内核和根文件系统 1.安装交叉编译工具 为什么要先安装交叉编译工具?由于我们的Linux操作系统是安装在嵌入式处理器平台上的,需要在主机上编译出开发板需 ...
- 嵌入式烧写Linux内核,嵌入式linux 内核和根文件系统烧写方式简介
总体来说,嵌入式Linux内核和根文件的引导与PC机差不多. 嵌入式linux内核和根文件系统可以存放在各种可能的存储设备中,一般情况下我们将内核和根文件系统直接烧入到Flash中(包括NOR和NAN ...
- 二十.Linux开发之根文件系统构建及过程详解
老规矩 有道云笔记地址: 详情看这里链接,记录太多,就不一一排版了. http://note.youdao.com/noteshare?id=15b6e982c2e66d0f47b1c787a49f4 ...
- linux根文件系统创建过程
步骤一:创建根文件系统基本目录 1.在home目录下创建:mkdir rootfs,然后, 2.在usr/下面建立子目录 3.在dev/下面建立字符设备文件 4安装/etc,系统所有配置文件都在这里, ...
- SigmaStar SSD202 openwrt 系统下ubi根文件系统挂载过程
关于UBI介绍可以参考官方文档 http://www.linux-mtd.infradead.org/doc/ubifs.html 下面是一张简介图,大概的介绍就是UBIFS依赖kernel UBI子 ...
- linux如何初始化根文件系统,搭建自己的Linux根文件系统
整体框架: 构建最小根文件系统: 1) 创建设备console和null: 首先,我们需要创建一个目录,自己命名. 这里我创建czg目录:mkdir czg.然后进入自己创建的这个文件夹目录,并创建d ...
最新文章
- navicat 连接oracle
- 深入理解android卷II 即将发布
- 四十四、Mysql的命令和PyMysql
- Vue 结合Element UI 实现导航的 router 属性 expected boolean,got string
- ITK:对多个线程上的数据进行操作以利用多核处理器
- 首届Apache Hadoop技术社区中国Meetup在京举办(附PPT)
- confluence 为合并的单元格新增一行
- imx6 mac地址设置
- Sharepoint学习笔记—Site Definition系列-- 1、创建Site Columns
- 腾讯三面:40亿个QQ号码如何去重?
- 笨办法学 Python · 续 练习 3:质量
- Leecode刷题热题HOT100(1)——两数之和
- 解析淘宝商城缘何更名“天猫”
- 编程之美-2.4 1的数目
- java 定义三维列表_java 多维数据定义
- 【小玩意】锟斤拷语加密器
- google手机连接WIFI后总会提示“无法连接互联网“的解决方法
- MOOC《Python网络爬虫和信息提取》(第11次)网络爬虫之框架(第4周)
- PDF如何转换成PPT?教你们几个简单方法
- MacBook Pro完整卸载及安装激活VMware Fusion13.0.0教程
热门文章
- 用AngularJS开发下一代Web应用pdf
- 1.swt/rap学习源码网址
- IOS_多线程_GET_POST_AFN_上传下载_视频播放
- 学习React之前你需要知道的的JavaScript基础知识
- ubuntu18 安装 mysql5.7
- Silverlight提示“Load 操作失败。远程服务器返回了错误: NotFound”
- apache 配置虚拟域名默认站点问题
- 使用GameKit实现IOS设备之间的蓝牙通信
- 《WCF技术内幕》翻译35:第2部分_第6章_通道:通道功能
- 集体智慧编程Python