了解完kernel启动以前的汇编之后我们来看看正式的c语言启动代码,也就是我们的start_kernel函数了。start_kernel相当大,里面每一个调用到的函数都足够我们伤脑筋了,我这里只是浅尝辄止的描述一下函数的功能,从而对kernel启动的过程有一个比较直观的了解。很多函数真正理解需要对linux相关体系有很深的了解,暂时没有时间深入,留待以后了。

说实话启动的代码看到现在唯一的感觉就是kernel的全局变量实在太多了,要了解一个过程跟踪一个变量的值的变化相当痛苦啊,不过耐心看下来,收获还是比较丰富的,对很多概念都有了一个比较直观的理解。闲话就不多说了,直接来上代码~~


smp_setup_processor_id();

//这个函数现在是空的;


lockdep_init();

//Runtime  locking correctness validator, see Documentation/lockdep_design.txt

debug_objects_early_init();

cgroup_init_early();

//Control group, read Documentation/cgroup.txt

local_irq_disable();

//使用arm cpsid i指令来禁止IRQ

early_boot_irqs_off();

early_init_irq_lock_class();

/* 基本上面几个函数就是初始化lockdep和cgroup,然后禁止IRQ,为后续的运行创造条件 */


lock_kernel();

/* 看这个函数的之前我们首先来了解一段知识,linux kernel默认是支持preemption(抢占)的。在SMP环境下为了实现kernel的锁定,kernel使用了一个BKL(big kernel lock)的概念,在初始化的过程中先锁定这个BKL,然后再继续进行其他启动或者初始化过程,这样就可以防止启动过程中被中断,执行到res_init以后,kernel会释放这个锁,这样就确保了整个start_kernel过程都不会被抢占或者中断。由此我们可以看到这主要是针对多处理器而言的,实际上如果只有一个处理器的话它也不会被中断或者被抢占。 */

/* 下面我们来看看这个函数的执行过程,在这里面能学到很多东西的:

int depth = current->lock_depth+1;

这个current实际上是一个宏,定义在arch/arm/include/asm/current.h里面,它实际是一个task_struct的结构体,这个结构体描述了这个task的一系列信息(task应该是linux进程调度的一个基本单位?)。linux下每个进程都有一个相对应的task_struct,这个task_struct有几个我们经常能看到的信息,一个就是PID,然后就是comm进程的名字,然后就是mm_struct,它定义了跟这个进程相关的所有申请的memory的管理。这个curren_thread_info()也是个很有意思的函数,直接读取SP来获得当前线程的结构体信息thread_info。thread_info和task_struct这两个结构体应该就代表了当前线程的所有信息。

初始化的lock_depth是-1,只有init task的lock_depth是0。

if (likely(!depth))

__lock_kernel();

这里判断是不是init task,如果是就会执行__lock_kernel();这个__lock_kernel首先禁止抢占,然后试着获得BKL,如果成功则直接返回,如果不成功首先判断是否禁止抢占成功了,如果成功了就用自旋锁去锁BKL。如果禁止抢占没有成功则在抢占有效的情况下去等待BKL,直到获得BKL。因为QC的片子不是SMP,所有这里第一次try的时候就直接成功了。

current->lock_depth = depth;

这个就没什么好说的了 */

/* 基本上来说这个lock_kernel就是禁止抢占,然后获得BKL,干了这么件事 */


tick_init();

//和时钟相关的初始化,好像是注册notify事件,没有仔细研究过


boot_cpu_init();

//这个实际上是在SMP环境下选择CPU,这里直接CPUID选择的是0号cpu


page_address_init();

//初始化high memory,在arm环境下实际上这个函数是空的,也就是说arm不支持high memory


printk(KERN_NOTICE);

printk(linux_banner);

//这里的KER_NOTICE是字符串<5>,不太明白它的意思。。。后面的linux_banner定义在kernel/init/version.c里面,这里的printk是门高深的学问,以后看console的时候会仔细分析


setup_arch(&command_line);

/* 这是一个重量级的函数了,会比较仔细地分析一下,主要完成了4个方面的工作,一个就是取得MACHINE和PROCESSOR的信息然或将他们赋值给kernel相应的全局变量,然后呢是对boot_command_line和tags接行解析,再然后呢就是memory、cach的初始化,最后是为kernel的后续运行请求资源。 */

/* 我们来仔细看看这个函数的实现:

setup_processor();

这个函数首先从arm寄存器里面取得cpu ID,然后调用lookup_processor_type来取得proc_info_list这个结构体。这个过程实际上和我们在head-common.S里面的过程是一样的,不知道这里为什么不直接去读switch_data里面已经保存好的数据反而又查询一遍是为什么?取得proc_info_list以后,将里面的内容一个个赋值给相应的全局变量,然后将CPU的信息打印出来。然后它会从arm寄存器里面获得cache的信息,并将cache的信息打印出来。最后它会调用cpu_proc_init()的函数,这个函数实际上定义在proc-v6.S里面,没有做任何事情。

mdesc = setup_machine(machine_arch_type);

首先这个machine_arch_type定义在生成的./include/asm-arm/mach-types.h里面,这个setup_macine实际上也是和上面的processor类似,都是调用head-common.S里面的函数,根据machine ID来获得Machine的信息,并将Machine name打印出来。

if (mdesc->soft_reboot)

reboot_setup("s");

设置reboot方式,默认是硬启动;

if (__atags_pointer)

tags = phys_to_virt(__atags_pointer);

else if (mdesc->boot_params)

tags = phys_to_virt(mdesc->boot_params);

这里首先判断head-common.S里面定义的__atags_pointer是不是为空,不为空的话说明bootloader设置了初始化参数,将参数的物理地址转化为虚拟地址,这里有一个覆盖,就是说可以在Machine desc里面对初始化参数的物理地址重新定位。

if (tags->hdr.tag != ATAG_CORE)

convert_to_tag_list(tags);

if (tags->hdr.tag != ATAG_CORE)

tags = (struct tag *)&init_tags;

首先判断是不是正确的atag格式,如果是以前老版本的param_struct格式会首先将其转换成tag格式,如果转换以后还是不对,则使用默认的init_tags,这里判断的过程都是根据结构体第一个值是不是ATAG_CORE.

if (mdesc->fixup)

mdesc->fixup(mdesc, tags, &from, &meminfo);

if (tags->hdr.tag == ATAG_CORE) {

if (meminfo.nr_banks != 0)

squash_mem_tags(tags);

save_atags(tags);

parse_tags(tags);

这里首先判断fixup函数指针,这里一般为空,如果不为空就会地用fixup来重新修改memory map,meminfo这个结构体定义在arch/arm/include/asm/setup.h里面,描述了内存块的信息,内存块的个数,每个内存块的起始地址和大小,如果修改了memory map则需要从atag参数里面去掉bootloader传过来的的memory map信息,然后是保存一下atag,这个保存函数在这里实际上是空的,没有做任何操作,最后是对atag参数进行解析。这里要说明一下这里的tags实际上是一个tag的数组或者说队列,里面有多个tag结构体,每一个结构体都是一个header加一个参数,具体的结构我们可以看看setup.h。对ATAG参数的解析全部定义在arch/arm/kernel/setup.c里面,首先在setup.c里面定义了一个类似于这样__tagtable(ATAG_CORE, parse_tag_core)的宏,这个宏实际上是声明了一个放在__tagtable_begin和__tagtable_end段之间结构体,这个结构体定义了这个一个参数类型,和对这个参数类型进行解析的函数。所有的参数解析我们都可以从setup.c里面找到相对应的函数,比如说对boot_commad_line的解析,从config文件得到的default_commad_line就会被ATAG里面获得commad_line给替换掉;再比如ramdisk,就会将ATAG里面的ramdisk的信息赋值给rd_image_start, rd_size等系统的全局变量。

init_mm.start_code = (unsigned long) _text;

init_mm.end_code   = (unsigned long) _etext;

init_mm.end_data   = (unsigned long) _edata;

init_mm.brk    = (unsigned long) _end;

这就就是对init_mm结构体进行赋值,具体不了解这些东西咋用的,但是就是将text和data段给赋值了。

memcpy(boot_command_line, from, COMMAND_LINE_SIZE);

boot_command_line[COMMAND_LINE_SIZE-1] = '/0';

parse_cmdline(cmdline_p, from);

这里的boot_command_line来自于与config文件里面的CONFIG_CMDLINE,也有可能被ATAG里面的boot参数给覆盖,获得command_line以后对command_line进行解析。这个解析的过程也是在setup.c里面进行的,它首先查找.early_param.init段里面注册的结构体,通过__early_param将early_param结构体注册到这些段里面,实际这个early_param很简单一个就是类似于"initrd="的字符串,用来与command_line里面的字符进行匹配,如果匹配到了就执行early_param里面的那个函数,并将匹配到的字符作为参数传给函数。举个例子比如说我们现在的commadline里面有一句initrd=0x11000000,然后我们首先在early_param.init段里面搜索时候有没有early_param->arg和这个initrd=匹配,找到了就执行它里面的func然后将这个initrd=的值作为参数传进去。

paging_init(mdesc);

这个函数是个大函数,具体内容没有仔细看,需要对arm MMU了解比较深,这里只贴一下source里面关于这个函数的注释:

/*

* paging_init() sets up the page tables, initialises the zone memory

* maps, and sets up the zero page, bad page and bad page tables.

*/

request_standard_resources(&meminfo, mdesc);

这个函数用来申请一些应该是内存资源,具体的内容没有仔细研究,看不大懂。。

cpu_init();

初始化CPU,这里主要是对arm寄存器cpsr的操作

init_arch_irq = mdesc->init_irq;

system_timer = mdesc->timer;

init_machine = mdesc->init_machine;

这里将体系结构相关的几个函数,中断,初始化,定时器之类的赋值给kernel全局变量;

conswitchp = &vga_con;

这里设置了关于console的一个变量,具体不知道怎么用的,以后看console的时候再仔细分析

early_trap_init();

不知道这个函数具体做什么用的。。。 */

/* 基本上我们可以总结出setup_arch主要将一些体系结构的相关信息来赋值给kernel的全局变量,包括cpu啊,machine啊,memory,cahce啊,然后kernel再根据这些函数或者变量来做相应的工作,而且很明显地可以看出来这个setup_arch和前面的head.S,head-common.S,proc-v6.S,board-msm7x27.c是紧密联系在一起的 */


差不多这边就讲到这里,下一篇将start_kernel的剩余部分讲完~~

http://blog.csdn.net/yili_xie/article/details/5750664

arm linux kernel启动之start_kernel的更多相关文章

  1. andriod and linux kernel启动流程

    虽然这里的Arm Linux kernel前面加上了Android,但实际上还是和普遍Arm linux kernel启动的过程一样的,这里只是结合一下Android的Makefile,讲一下boot ...

  2. ARM linux的启动部分源代码简略分析【转】

    转自:http://www.cnblogs.com/armlinux/archive/2011/11/07/2396784.html ARM linux的启动部分源代码简略分析 以友善之臂的mini2 ...

  3. linux kernel启动流程

    linux kernel启动是从./init/main.c中开始的,其大概流程是: 1. 调用start_kernel()函数: 2. start_kernel()调用rest_init()函数: 3 ...

  4. LINUX KERNEL启动参数

    LINUX KERNEL启动参数 在Linux中,给kernel传递参数以控制其行为总共有三种方法: 1.build kernel之时的各个configuration选项. 2.当kernel启动之时 ...

  5. Linux kernel启动选项(参数)(转)

    Linux kernel启动选项(参数)  转载链接https://www.cnblogs.com/linuxbo/p/4286227.html 在Linux中,给kernel传递参数以控制其行为总共 ...

  6. arm linux kernel 从入口到start_kernel 的代码分析

    参考资料: <ARM体系结构与编程> <嵌入式Linux应用开发完全手册> Linux_Memory_Address_Mapping http://www.chinaunix. ...

  7. linux内核可以接受的参数 | Linux kernel启动参数 | 通过grub给内核传递参数

    在Linux中,给kernel传递参数以控制其行为总共有三种方法: 1.build kernel之时的各个configuration选项. 2.当kernel启动之时,可以参数在kernel被GRUB ...

  8. Linux kernel启动选项(参数)

    在Linux中,给kernel传递参数以控制其行为总共有三种方法: 1.build kernel之时的各个configuration选项. 2.当kernel启动之时,可以参数在kernel被GRUB ...

  9. ARM linux内核启动时几个关键地址【转】

    转自:http://www.cnblogs.com/armlinux/archive/2011/11/06/2396787.html 1.       内核启动地址1.1.   名词解释ZTEXTAD ...

随机推荐

  1. margin系列之keyword auto

    本系列摘自  px; margin: auto; /* 或者 margin: 0 auto; */ } HTML: <div id="demo"> <p>恩 ...

  2. python3编码问题

    继续收集python3编码问题相关资料 资料来源  鹏程的新浪博客(转载)http://blog.sina.com.cn/s/blog_6d7cf9e50102vo90.html  这篇鹏程老师写的关 ...

  3. Project not selected to build for this solution configuration.

    Project not selected to build for this solution configuration.   When you upgrade your older solutio ...

  4. post请求json内容丢失问题

    今天在项目组用json传输数据 post方法提交 发现传输过去的数据json内的+ 号被直接干掉了. 后来传输之前直接先编码. 接收端: public void ProcessRequest(Http ...

  5. css清除浮动的几种方法整理

    四种清除浮动方法如下: 1.使用空标签清除浮动.空标签可以是div标签,也可以是P 标签.这种方式是在需要清除浮动的父级元素内部的所有浮动元素后添加这样一个标签 清除浮动,并为其定义CSS代码:cle ...

  6. WINDOWS 2008 SERVER域用户自动登陆

    The user I wanted to auto-logon as didn’t have a password, this reg hack worked instead: HKEY_LOCAL_ ...

  7. [jobdu]二进制中1的个数

    做法是n&(n-1).据说还有变态的查表法:http://www.cnblogs.com/graphics/archive/2010/06/21/1752421.html.最后,居然必须用sc ...

  8. win7桌面图标小盾牌怎么去掉(2种方法)

    很多用户都会在桌面上放置一些常用的程序图标,由于win7系统提高了系统安全性,新增用户帐户控制,所以会在图标上显示小盾牌,表示需要管理员权限打开.不少win7 32位旗舰版用户觉得这个小盾牌很碍眼,那 ...

  9. 《linux程序设计》笔记 第一章 入门

    linux程序存放位置linux主要有一下几个存放程序的目录: /bin    系统启动程序目录 /usr/bin 用户使用的标准程序 /usr/local/bin   用于存放软件安装目录 /usr ...

  10. 精通phthon的条件

    1. 熟知主流硬件体系(x86, x64)2. 熟知 CPython 的具体实现,如若可能至少通读源码三遍以上3. 熟知每条 Python bytecode 如何被解释执行4. 熟知每条 Python ...