第3阶段——内核启动分析之prepare_namespace()如何挂载根文件系统和mtd分区介绍(6)...
内核启动并初始化后,最终目的是像Windows一样能启动应用程序,在windows中每个应用程序都存在C盘、D盘等,而linux中每个应用程序是存放在根文件系统里面,那么挂载根文件系统在哪里,怎么实现最终目的运行应用程序?
1.内核运行应用程序步骤:
1.1首先是进入stext函数启动内核:
ENTRY(stext): ... ... //启动内核b start_kernel //跳转start_kernel()
1.2然后进入strat_kernel()初始化:
asmlinkage void __init start_kernel(void) {...setup_arch(&command_line); //解析uboot传入的启动参数setup_command_line(command_line); //解析uboot传入的启动参数..../*查找内核参数*/parse_early_param(){do_early_param(); //从__setup_start到__setup_end查找early非0的函数,后面会分析} /*查找内核参数*/unknown_bootoption() {obsolete_checksetup(); //从__setup_start到__setup_end查找early为0的函数,后面会分析}...rest_init(); //进入rest_init() }
1.3.进入rest_init()启动init进程
static void noinline __init_refok rest_init(void) {kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); //启动init进程,然后进入kernel_init中启动应用程序... ...}
1.4进入kernel_init
static int __init kernel_init(void * unused) {... ...prepare_namespace() //进入prepare_namespace函数,挂载系统{... ... / /通过解析出来的命令行参数” root=/dev/mtdblock3”来挂接根文件系统 mount_root(); //开始挂载} init_post() //进入init_post() 函数,运行应用程序{ /* 打开dev/console,并提供输入、输出、错误提示 */ if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) printk(KERN_WARNING "Warning: unable to open an initial console.\n");(void) sys_dup(0);(void) sys_dup(0); ... ... run_init_process("/sbin/init"); //执行应用程序run_init_process("/etc/init"); //执行应用程序run_init_process("/bin/init"); //执行应用程序run_init_process("/bin/sh"); //执行应用程序 }
接下来我们就开始详细分析prepare_namespace()如何挂载文件系统
4 首先分析1.3节的prepare_namespace()函数中怎么挂接的文件系统”root=/dev/mtdblock3”
prepare_namespace()代码如下 (mtdblock3:mtd分区3(kernel分区))
void __init prepare_namespace(void){... ... if (saved_root_name[0]) //判断saved_root_name[0]数组是否为空 { root_device_name = saved_root_name; if (!strncmp(root_device_name, "mtd", 3)) { //比较root_device_name数组是否已mtd开头?mount_block_root(root_device_name, root_mountflags); goto out; } //是mtd,则跳转到out,直接挂载ROOT_DEV = name_to_dev_t(root_device_name); if (strncmp(root_device_name, "/dev/", 5) == 0) //比较是不是已/dev/开头 root_device_name += 5; //是的话,+5找到mtd开头 }is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; if (initrd_load()) goto out; if (is_floppy && rd_doload && rd_load_disk(0)) ROOT_DEV = Root_RAM0; mount_root(); //将实际文件系统挂载到rootfs的/root目录out:sys_mount(".", "/", NULL, MS_MOVE, NULL);sys_chroot(".");security_sb_post_mountroot(); }
从上面代码得出,saved_root_name数组通过名字可以得出,是用来保存root文件系统的名字” /dev/mtdblock3”.
5 bootags参数又是怎么保存到数组的呢?,通过搜索”saved_root_name”,找到如下代码:
static int __init root_dev_setup(char *line) {strlcpy(saved_root_name, line, sizeof(saved_root_name)); //复制saved_root_name数组return 1; } __setup("root=", root_dev_setup);
其中root_dev_setup()函数是用来将line数组中的数据统统复制到saved_root_name数组中
__setup("root=", root_dev_setup);中有”root=”,猜测下,这个估计就是用来匹配命令行中以”root=”开头的字符串,然后再将” root=/dev/mtdblock3”中的”/dev/mtdblock3”放在saved_root_name数组中
6.接下来分析上面的__setup("root=", root_dev_setup)宏定义
我们搜索__setup宏,找到它在include/linux/init.h中定义:
#define __setup_param(str, unique_id, fn, early) \ //定义__setup_param(str, unique_id, fn, early) /*定义字符串数组__setup_str_##unique_id[]=str; \表示还在define中 */ static char __setup_str_##unique_id[] __initdata = str; \ //相当于: __setup_str_ root_dev_setup[]="root=" /*定义结构体obs_kernel_param型__setup_##unique_id*/ static struct obs_kernel_param __setup_##unique_id\ __attribute_used__ \__attribute__((__section__(".init.setup"))) \ //设置.init.setup段__attribute__((aligned((sizeof(long))))) \= { __setup_str_##unique_id, fn, early } //将"root=",root_dev_setup,0 放在 .init.setup段中#define __setup(str, fn) \ //定义__setup(str, fn)使用__setup_param(str, fn, fn, 0)__setup_param(str, fn, fn, 0)
最终__setup("root=", root_dev_setup)宏= { __setup_str_ root_dev_setup[], root_dev_setup , 0 };
在.init.setup段中存放了3个成员,第一个成员是字符串数组等于”root=”,第二个成员是一个函数,第三个成员early=0;
其中.init.setup段在vmlinux.lds中使用(.init.setup段用于存放特殊的内容,比如命令行参数).
vmlinux.lds部分代码如下:
311 . = ALIGN(16); 312 __setup_start = .; 313 *(.init.setup) //存放.init.setup段 314 __setup_end = .;
7.接下来分析宏__setup("root=", root_dev_setup);又是怎么被调用的
由于通过宏”__setup("root=", root_dev_setup);”最终被存在了.init.setup段里,
所以首先搜索”__setup_start”,发现在init/main.c中do_early_param函数和obsolete_checksetup函数都使用了它
7.1先来分析do_early_param函数,首先我们看看它被谁调用
搜索do_early_param,发现它被parse_early_param()函数调用,如下图:
然后搜索parse_early_param(),发现它在start_kernel函数中使用,如下图:
得出:在内核启动start_kernel()中会处理这个do_early_param函数.
7.1.1接下来分析do_early_param源码:
static int __init do_early_param(char *param, char *val) { struct obs_kernel_param *p; //定义obs_kernel_param结构体指针*p for (p = __setup_start; p < __setup_end; p++) //查找.init.setup段的内容 {if (p->early && strcmp(param, p->str) == 0) { if (p->setup_func(val) != 0) //处理early非0的函数printk(KERN_WARNING"Malformed early option '%s'\n", param); }} }
上面obs_kernel_param的结构体定义如下,刚好对应了
struct obs_kernel_param {const char *str; //__setup_str_ root_dev_setup[]=”root=”int (*setup_func)(char *); // root_dev_setup(char *line)int early; // early=0 };
__setup("root=", root_dev_setup)宏= { __setup_str_ root_dev_setup[], root_dev_setup , 0 }中的3个成员。
由于__setup("root=", root_dev_setup)的early=0,所以if (p->early && strcmp(param, p->str) == 0)永远不成立.
所以在内核启动strat_kernel()函数中会通过do_early_param函数是处理early不为0的函数。
7.2然后分析obsolete_checksetup函数,首先我们看看它被谁调用
搜索obsolete_checksetup,发现它被unknown_bootoption ()函数调用:
然后搜索unknown_bootoption (),发现它在start_kernel函数中使用, 如下图:
得出:在内核启动start_kernel()中会处理这个do_early_param函数.
7.2.1接下来分析obsolete_checksetup源码:
static int __init obsolete_checksetup(char *line) { struct obs_kernel_param *p; //定义obs_kernel_param型结构体指针 int had_early_param = 0;p = __setup_start; do {int n = strlen(p->str);if (!strncmp(line, p->str, n)) //确定是否有内容{if (p->early) { //early非0,则不执行函数 if (line[n] == '\0' || line[n] == '=')had_early_param = 1;} else if (!p->setup_func) { // 处理early为0的函数,printk(KERN_WARNING "Parameter %s is obsolete,"" ignored\n", p->str);return 1; } else if (p->setup_func(line + n)) //处理early为0的函数return 1;}p++;} while (p < __setup_end); //从__setup_start到__setup_end查找return had_early_param; }
通过上面代码分析得出:
__setup("root=", root_dev_setup)宏= { __setup_str_ root_dev_setup[], root_dev_setup , 0 }中的第三个成员early=0, 会执行root_dev_setup()函数,然后将文件系统目录拷贝到全局变量saved_root_name[]数组中,使后面的函数来挂载文件系统.
所以在内核启动strat_kernel()函数中会通过obsolete_checksetup函数处理early为0的函数。
8 root=/dev/mtdblock3 分析:
在flash中没有分区表,在内核中,mtdblock3又在哪里体现出来的?
和uboot一样,它也是在内核代码中已经写好了的,
在内核中可以通过启动内核,从串口上可以看到分区表,如下图:
从上面得出,在flash中定义了4大分区:
bootloader :存放u-boot
boot parameters :存放一些可以设置的参数,供u-boot使用
kernel :存放内核区
root filesystem :根文件系统,挂载(mount)后才能使用文件系统中的应用程序
8.1它们又是在内核代码中哪里体现出来的呢?
1. 在linux-2.6.22.6目录下通过 grep "\"bootloader\"" * -nR 搜索分区代码,如下图
由于使用的是ARM架构,CPU2440,所以找到上面红线处的行, 才是我们需要的。
然后进入arch/arm/plat-s3c24xx/common-smdk.c中,找到120行,代码如下:
static struct mtd_partition smdk_default_nand_part[] = {[0] = { // mtdblock0.name = "bootloader",.size = 0x00040000,.offset = 0,},[1] = { // mtdblock1.name = "params",.offset = MTDPART_OFS_APPEND, //表示紧跟着前面的地址后面,为偏移地址,= 0x00040000.size = 0x00020000,},[2] = { // mtdblock2.name = "kernel",.offset = MTDPART_OFS_APPEND, //表示紧跟着前面的地址后面,为偏移地址,= 0x00060000.size = 0x00200000,},[3] = { // mtdblock3.name = "root",.offset = MTDPART_OFS_APPEND, //表示紧跟着前面的地址后面,为偏移地址,= 0x00260000.size = MTDPART_SIZ_FULL,}};
接下来开始分析init_post()如何启动第1个程序
第3阶段——内核启动分析之prepare_namespace()如何挂载根文件系统和mtd分区介绍(6)...相关推荐
- 内核启动分析(三)——zImage 解压缩阶段
在上阶段,主要是U-BOOT 向内核传递一些参数.而这些参数是通过 struct tag来传递的.U-boot 把要传递给 kernel 的东西保存在 struct tag 数据结构中,启 ...
- linux内核启动分析 三,Linux内核分析 实验三:跟踪分析Linux内核的启动过程
贺邦 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一. 实验过程 ...
- uboot源码——内核启动分析
以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除. 参考资料:http://www.cnblogs.com/biaohc/p/6403863.html 总结:uboot启动linux内核的整个 ...
- linux内核启动分析(一)
文章目录 1.HEAD 1.preserve_boot_args 1.1 __inval_dcache_area 2.el2_setup 3. set_cpu_boot_mode_flag 4. __ ...
- linux 内核启动Initramfs与initrd 及其挂载
Initramfs/initrd则是填充(仅仅是释放文件到rootfs根目录)/扩充(通过挂载其他文件系统类型到rootfs指定目录)rootfs的关键,以保证Linux系统的后续启动. 1.init ...
- Linux内核3.0移植并基于Initramfs根文件系统启动
Linux内核移植与启动 Target borad:FL2440 Bootloader:U-boot-2010.09 交叉编译器:buildroot-2012.08 1.linux内核基础知识 首先, ...
- u-boot内核启动分析
菜单的实现函数 - cmd_menu.c pc机上都有分区,但是在嵌入式设备中的flash没有分区 所谓的嵌入式中的分区就是使用代码进行写死 sourceinsight 中搜索函数的快捷键是: Alt ...
- Hi3518ev200:内核启动分析
1)从DDR地址0x82000000加载内核镜像 hisilicon # bootm 82000000 ## Booting kernel from Legacy Image at 82000000 ...
- AR9331中Linux内核启动中与IRQ中断相关的文件
先列出框架,具体后继再来分析. 首先是lds文件,该文件设置了各个section在FLASH或RAM中的先后顺序. 位于~/openwrt1407/build_dir/target-mips_34kc ...
最新文章
- Android ListView 删除 item
- 使用 Matplotlib 这么久,竟不知道数据可以动起来
- 有了java为什么还需要groovy_Groovy创始人:Java面临终结 Scala将取而代之
- PhantomJS快速入门教程(转)
- Python 开发的 10 个小贴士,你知道几个?
- linux 关机命令总结
- 100个犯人和一个亮灯泡_每个人都在一个开放的组织中更换灯泡
- Android中使用Fragment实现标题栏(不可滑动)
- swagger注解介绍
- 马上:网络故障排查的思路和方法
- Python字符串地常规操作:取值,切片,查找,替换,分割,拼接,转义
- 文章复现 子宫腺肌病在位内膜和异位病灶的单细胞转录组分析
- 怎么让热图显示基因名_教你画一个掰弯的热图(Heatmap),展示更多的基因表达量...
- Android之如何分析手机系统相册图片和视频删除后保存的位置
- 图仓库-图片记忆23种设计模式
- 江苏省高中计算机合格性考试题目,江苏省2020年普通高中学业水平合格性考试试卷(含答案)...
- 全国计算机一级考试理论部分,全国计算机等级考试一级理论题
- 篮球社交小程序开发功能
- 冯诺依曼计算机主机,冯诺依曼结构计算机包括哪五大部分?
- Facebook 是如何抄袭并逼死其他创业公司的?