记录自己学习android系统启动以及 recovery过程(2)----------kernel

来源:互联网 发布:什么软件可以越狱的 编辑:程序博客网 时间:2024/04/29 02:06

记录自己学习android系统启动以及 recovery过程(1)----------kernel

kernel阶段,代码比较复杂,才看懂基本流程。。。。。

在kernel阶段,会根据传递进来的参数,进行初始化并挂载root

1. uboot->kernel的参数解析模式

解析tag函数流程: start_kernel->setup_arch->parse_tags

以initrc举例,在uboot中,配置函数为:

static void setup_initrd_tag (bd_t *bd, ulong initrd_start, ulong initrd_end){/* an ATAG_INITRD node tells the kernel where the compressed * ramdisk can be found. ATAG_RDIMG is a better name, actually. */params->hdr.tag = ATAG_INITRD2;params->hdr.size = tag_size (tag_initrd);params->u.initrd.start = initrd_start;params->u.initrd.size = initrd_end - initrd_start;params = tag_next (params);}

在kernel中,存在相应的解析函数:

static int __init parse_tag_initrd2(const struct tag *tag){phys_initrd_start = tag->u.initrd.start;phys_initrd_size = tag->u.initrd.size;return 0;}__tagtable(ATAG_INITRD2, parse_tag_initrd2);
可以看到,这两个函数都使用到了相同的宏ATAG_INITRD2,在kernel中,__tagtable标志使得ATAG_INITRD2, parse_tag_initrd2被注册进了一个结构体tagtable,并在解析tag时,通过比较相应的宏是否一致,调用注册的函数进行解析

在上述函数中:phys_initrd_start,phys_initrd_size是被初始化为0的,因此,当传进来的tag中,不含有ATAG_INITRD2时,这两个变量均等于0。

2. 初始化rootfs文件系统

   初始化rootfs文件系统流程:start_kernel->vfs_caches_init->mnt_init

   mnt_init函数如下:

void __init mnt_init(void){......init_rwsem(&namespace_sem);......err = sysfs_init();......fs_kobj = kobject_create_and_add("fs", NULL);......init_rootfs();            /*注册rootfs文件系统*/init_mount_tree();        /*这步执行结束后,文件系统中存在目录‘/’,以后的文件系统,会挂载在这个目录下*/} 
init_rootfs:

int __init init_rootfs(void){int err;err = bdi_init(&ramfs_backing_dev_info);if (err)return err;err = register_filesystem(&rootfs_fs_type);              /*rootfs 结构体*/if (err)bdi_destroy(&ramfs_backing_dev_info);return err;} 
rootfs结构体:

static struct file_system_type rootfs_fs_type = {.name= "rootfs",.get_sb= rootfs_get_sb,                        /*在mount rootfs时,会用到*/.kill_sb= kill_litter_super,};
执行完init_rootfs后,完成了系统中的kobject以及filesystem中rootfs相关的注册
init_mount_tree函数执行流程:  init_mount_tree->do_kern_mount->vfs_kern_mount->rootfs_get_sb(type->get_sb)->ramfs_fill_super->d_alloc_root

d_alloc_root函数:

/** * d_alloc_root - allocate root dentry * @root_inode: inode to allocate the root for * * Allocate a root ("/") dentry for the inode given. The inode is * instantiated and returned. %NULL is returned if there is insufficient * memory or the inode passed is %NULL. */ struct dentry * d_alloc_root(struct inode * root_inode){struct dentry *res = NULL;if (root_inode) {static const struct qstr name = { .name = "/", .len = 1 };res = d_alloc(NULL, &name);if (res) {res->d_sb = root_inode->i_sb;res->d_parent = res;d_instantiate(res, root_inode);}}return res;}

这个函数看注释知道干嘛,具体执行过程,暂时不了。。。。

此时,rootfs的初始化,在文件系统中已经存在‘/'目录,并在后续用来mount 需要加载的其他文件系统

3. ramdisk

ramdisk执行流程:start_kernel->rest_init->kernel_init

kernel_init函数:

static int __init kernel_init(void * unused){/* * Wait until kthreadd is all set-up. */wait_for_completion(&kthreadd_done);lock_kernel();/* * init can allocate pages on any node */set_mems_allowed(node_states[N_HIGH_MEMORY]);/* * init can run on any cpu. */set_cpus_allowed_ptr(current, cpu_all_mask);/* * Tell the world that we're going to be the grim * reaper of innocent orphaned children. * * We don't want people to have to make incorrect * assumptions about where in the task array this * can be found. */init_pid_ns.child_reaper = current;cad_pid = task_pid(current);smp_prepare_cpus(setup_max_cpus);do_pre_smp_initcalls();                /*这个函数对注册的initcall进行一次执行,其中包含populate_rootfs*/start_boot_trace();smp_init();sched_init_smp();printk(KERN_INFO "$$$do_basic_setup start\n");do_basic_setup();printk(KERN_INFO "$$$do_basic_setup end\n");/* Open the /dev/console on the rootfs, this should never fail */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);/* * check if there is an early userspace init.  If yes, let it do all * the work */        /*查看ramdisk需要执行的函数或者cmd*/if (!ramdisk_execute_command)ramdisk_execute_command = "/init";if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {ramdisk_execute_command = NULL;prepare_namespace();            /*当当前的目录下,没有/init可以执行,则调用这个函数,cmdline传递进来的(root=/dev/mmcblk0px)就是这一步被正确挂载的*/}/* * Ok, we have completed the initial bootup, and * we're essentially up and running. Get rid of the * initmem segments and start the user-mode stuff.. */init_post();       /*执行当前目录下的/init函数*/return 0;}
populate_rootfs函数:

static int __init populate_rootfs(void){[1]char *err = unpack_to_rootfs(__initramfs_start,  __initramfs_end - __initramfs_start);if (err)panic(err);/* Failed to decompress INTERNAL initramfs */[2]if (initrd_start) {#ifdef CONFIG_BLK_DEV_RAMint fd;printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");err = unpack_to_rootfs((char *)initrd_start,initrd_end - initrd_start);if (!err) {free_initrd();return 0;} else {clean_rootfs();unpack_to_rootfs(__initramfs_start, __initramfs_end - __initramfs_start);}printk(KERN_INFO "rootfs image is not initramfs (%s)""; looks like an initrd\n", err);fd = sys_open("/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();}#elseprintk(KERN_INFO "Unpacking initramfs...\n");[3]err = unpack_to_rootfs((char *)initrd_start,initrd_end - initrd_start);if (err)printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);free_initrd();#endif}return 0;}

这个函数详细功能:

      [1] __initramfs_start, __initramfs_end这个变量是在link script里面定义的,编译进linux内核的ramdisk部分,如果存在,则解压在'/'下

      [2] initrd_start是通过tags函数得到的phys_initrd_start,进行PA->VA后的虚拟地址,流程是start_kernel->setup_arch->paging_init->bootmem_init->bootmem_reserve_initrd。因此可知,在uboot传递进initrd tag时,会得到有效地initrd_start,否则无效

      [3](什么是CPIO?。。。)同理,解压传递进来的initrd至'/'


在kernel_init函数中,当执行sys_access函数,如果当前目录下没有/init,就执行prepare_namespace函数。

正常boot模式下,挂载的ramdisk是由system.img编译而来,在root目录下,存在/init,但是,当设置为recovery是,由于不存在ramdisk,因此,此时需要执行prepare_namespace函数。

prepare_namespace函数:

/* * Prepare the namespace - decide what/where to mount, load ramdisks, etc. */void __init prepare_namespace(void){int is_floppy;if (root_delay) {printk(KERN_INFO "Waiting %dsec before mounting root device...\n",       root_delay);ssleep(root_delay);}/* * wait for the known devices to complete their probing * * Note: this is a potential source of long boot delays. * For example, it is not atypical to wait 5 seconds here * for the touchpad of a laptop to initialize. */wait_for_device_probe();md_run_setup();if (saved_root_name[0]) {[1]root_device_name = saved_root_name;if (!strncmp(root_device_name, "mtd", 3) ||    !strncmp(root_device_name, "ubi", 3)) {mount_block_root(root_device_name, root_mountflags);goto out;}[2]ROOT_DEV = name_to_dev_t(root_device_name);[3]if (strncmp(root_device_name, "/dev/", 5) == 0)root_device_name += 5;}[4]if (initrd_load())goto out;/* wait for any asynchronous scanning to complete */if ((ROOT_DEV == 0) && root_wait) {printk(KERN_INFO "Waiting for root device %s...\n",saved_root_name);while (driver_probe_done() != 0 ||(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)msleep(100);async_synchronize_full();}is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;if (is_floppy && rd_doload && rd_load_disk(0))ROOT_DEV = Root_RAM0;[5]mount_root();out:devtmpfs_mount("dev");[6]sys_mount(".", "/", NULL, MS_MOVE, NULL);sys_chroot(".");}
[1] 其中的saved_root_name由来:系统注册的commandline函数,解析’root=‘关键字:如下
    
static int __init root_dev_setup(char *line){strlcpy(saved_root_name, line, sizeof(saved_root_name));return 1;}__setup("root=", root_dev_setup);
[2]得到设备号

[3]此时root_device_name就是“mmcblk0px”

[4]initrd_load     后续详细研究,似乎是读取/initrd.image不成功,返回0

[5]mount_root   将ROOT_DEV上的文件mount到/root目录下,mount过程如下:mount_root->mount_block_root->do_mount_root->sys_mount

执行结束后,通过函数

[6]mount当前目录为根目录,替换rootfs,因此,此时目录下,存在/init

4.init_post

init_post函数:

/* This is a non __init function. Force it to be noinline otherwise gcc * makes it inline to init() and it becomes part of init.text section */static noinline int init_post(void)__releases(kernel_lock){/* need to finish all async __init code before freeing the memory */async_synchronize_full();free_initmem();unlock_kernel();mark_rodata_ro();system_state = SYSTEM_RUNNING;numa_default_policy();current->signal->flags |= SIGNAL_UNKILLABLE;if (ramdisk_execute_command) {run_init_process(ramdisk_execute_command);printk(KERN_WARNING "Failed to execute %s\n",ramdisk_execute_command);}/* * We try each of these until one succeeds. * * The Bourne shell can be used instead of init if we are * trying to recover a really broken machine. */if (execute_command) {run_init_process(execute_command);printk(KERN_WARNING "Failed to execute %s.  Attempting ""defaults...\n", execute_command);}run_init_process("/sbin/init");run_init_process("/etc/init");run_init_process("/bin/init");run_init_process("/bin/sh");panic("No init found.  Try passing init= option to kernel. "      "See Linux Documentation/init.txt for guidance.");}

[1]执行ramdisk_execute_command,

此时,由于ramdisk_execute_command所指向的/init,在system image和recovery image两种模式下,并不一样

执行结果就是,一个执行system,一个执行recovery


综上:

在kernel启动后

1.会对uboot传递进来的参数进行分析,并对相应的变量初始化

2.挂载root,此处对ramdisk的挂载模式进行区分。

3.执行root目录下的/init

原创粉丝点击