<meta name="keywords" content="庄家克星时时彩,嵌入式ARM,Linux,kernel启动历程" />
检查: 3221|回复: 0
打印 上一主题 下一主题

[提问] 嵌入式ARM Linux kernel启动历程之浅尝辄止剖析start_kernel函数 [复制链接]

xyd2018 (离线)
积分
1499
帖子
300
跳转到指定楼层
楼主
揭晓于 2019-3-14 10:05:30 |只看该作者 |倒序浏览
要害词: 嵌入式ARM , Linux , kernel启动历程
明确了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是慎密联系在一起的 */


信盈达靠手艺打天下
以下课程可收费试听C语言电子PCBSTM32LinuxFPGA、JAVA、安卓等。
想学习的你和我联系预定便可以收费听课了。
宋工企鹅号:35--24-65--90-88   Tel/WX:173--17--95--19--08


您须要登录后才可以揭晓议论 登录 | 急速注册

关于我们  -  服务条目  -  应用指南  -  站点舆图  -  友谊链接  -  联系我们
庄家克星时时彩-时时彩qq群-时时彩平台推荐 © 版权一切   | 京公网安备110108881021702
回顶部