Linux移植之内核启动过程start_kernel函数简析

发布者:温暖阳光最新更新时间:2024-08-26 来源: cnblogs关键字:Linux移植  start_kernel函数 手机看文章 扫描二维码
随时随地手机看文章

在Linux移植之内核启动过程引导阶段分析中从arch/arm/kernel/head.S开始分析,最后分析到课start_kernel这个C函数,下面就简单分析下这个函数,因为涉及到Linux的内容较多,这里只是简单介绍下内核启动流程。先看一下内核启动的流程框图,截图来自《嵌入式Linux应用开发完全手册》。内核引导阶段已经分析过,接下来分析一下内核启动的第二阶段。

1、start_kernel函数全局概览

2、start_kernel函数调用层次

 

1、start_kernel函数全局概览,对start_kernel作一下粗略注释。

打开initMain.c ,下面主要分析处理UBOOT传入的参数,其中r1是传入的第一个参数存放的地址,里面存放着机器类型ID,已经处理过了;r2是传入的第二个参数,它存放着tag列表数据的地址。先看一下整个start_kernel函数,以下程序参考自http://www.cnblogs.com/lifexy/p/7366782.html



  1 asmlinkage void __init start_kernel(void) 

  2 

  3 { 

  4   char * command_line; 

  5   extern struct kernel_param __start___param[], __stop___param[];  

  6 

  7   smp_setup_processor_id();  //来设置smp process id,当然目前看到的代码里面这里是空的

  8   

  9   unwind_init(); 

 10 

 11 //lockdep是linux内核的一个调试模块,用来检查内核互斥机制尤其是自旋锁潜在的死锁问题。  

 12 //自旋锁由于是查询方式等待,不释放处理器,比一般的互斥机制更容易死锁,  

 13 //故引入lockdep检查以下几种情况可能的死锁(lockdep将有专门的文章详细介绍,在此只是简单列举):  

 14 //  

 15 //·同一个进程递归地加锁同一把锁;  

 16 //  

 17 //·一把锁既在中断(或中断下半部)使能的情况下执行过加锁操作,  

 18 // 又在中断(或中断下半部)里执行过加锁操作。这样该锁有可能在锁定时由于中断发生又试图在同一处理器上加锁;  

 19 //  

 20 //·加锁后导致依赖图产生成闭环,这是典型的死锁现象。  

 21    lockdep_init(); 

 22 //关闭当前CUP中断  

 23 local_irq_disable(); 

 24 

 25 //修改标记early_boot_irqs_enabled;  

 26 //通过一个静态全局变量 early_boot_irqs_enabled来帮助我们调试代码,  

 27 //通过这个标记可以帮助我们知道是否在”early bootup code”,也可以通过这个标志警告是有无效的终端打开  

 28 early_boot_irqs_off(); 

 29 

 30 //每一个中断都有一个IRQ描述符(struct irq_desc)来进行描述。  

 31 //这个函数的主要作用是设置所有的 IRQ描述符(struct irq_desc)的锁是统一的锁,  

 32 //还是每一个IRQ描述符(struct irq_desc)都有一个小锁。  

 33 early_init_irq_lock_class(); 

 34 /*

 35 

 36  * Interrupts are still disabled. Do necessary setups, then

 37  * enable them

 38  */ 

 39 // 大内核锁(BKL--Big Kernel Lock)  

 40 //大内核锁本质上也是自旋锁,但是它又不同于自旋锁,自旋锁是不可以递归获得锁的,因为那样会导致死锁。  

 41 //但大内核锁可以递归获得锁。大内核锁用于保护整个内核,而自旋锁用于保护非常特定的某一共享资源。  

 42 //进程保持大内核锁时可以发生调度,具体实现是:  

 43 //在执行schedule时,schedule将检查进程是否拥有大内核锁,如果有,它将被释放,以致于其它的进程能够获得该锁,  

 44 //而当轮到该进程运行时,再让它重新获得大内核锁。注意在保持自旋锁期间是不运行发生调度的。  

 45 //需要特别指出,整个内核只有一个大内核锁,其实不难理解,内核只有一个,而大内核锁是保护整个内核的,当然有且只有一个就足够了。  

 46 //还需要特别指出的是,大内核锁是历史遗留,内核中用的非常少,一般保持该锁的时间较长,因此不提倡使用它。  

 47 //从2.6.11内核起,大内核锁可以通过配置内核使其变得可抢占(自旋锁是不可抢占的),这时它实质上是一个互斥锁,使用信号量实现。  

 48 //大内核锁的API包括:  

 49 //  

 50 //void lock_kernel(void);  

 51 //  

 52 //该函数用于得到大内核锁。它可以递归调用而不会导致死锁。  

 53 //  

 54 //void unlock_kernel(void);  

 55 //  

 56 //该函数用于释放大内核锁。当然必须与lock_kernel配对使用,调用了多少次lock_kernel,就需要调用多少次unlock_kernel。  

 57 //大内核锁的API使用非常简单,按照以下方式使用就可以了:  

 58 //lock_kernel(); //对被保护的共享资源的访问 … unlock_kernel();  

 59 //http://blog.csdn.net/universus/archive/2010/05/25/5623971.aspx  

 60   lock_kernel(); 

 61 

 62 //初始化time ticket,时钟  

 63   tick_init(); 

 64 

 65 //函数 tick_init() 很简单,调用 clockevents_register_notifier 函数向 clockevents_chain 通知链注册元素:  

 66 // tick_notifier。这个元素的回调函数指明了当时钟事件设备信息发生变化(例如新加入一个时钟事件设备等等)时,  

 67 //应该执行的操作,该回调函数为 tick_notify   

 68 //http://blogold.chinaunix.net/u3/97642/showart_2050200.html  

 69   boot_cpu_init();

 70 

 71 //初始化页地址,当然对于arm这里是个空函数  

 72 //http://book.chinaunix.net/special/ebook/PrenticeHall/PrenticeHallPTRTheLinuxKernelPrimer/0131181637/ch08lev1sec5.html  

 73   page_address_init(); 

 74 

 75 /*打印KER_NOTICE,这里的KER_NOTICE是字符串<5>*/

 76   printk(KERN_NOTICE);

 77 

 78 /*打印以下linux版本信息:       

 79 “Linux version 2.6.22.6 (book@book-desktop) (gcc version 3.4.5) #1 Fri Jun 16 00:55:53 CST 2017” */

 80  printk(linux_banner);

 81       

 82 //系结构相关的内核初始化过程,处理uboot传递进来的atag参数( setup_memory_tags()和setup_commandline _tags() )  

 83 //http://www.cublog.cn/u3/94690/showart_2238008.html  

 84 setup_arch(&command_line); 

 85 

 86 //处理启动命令,这里就是设置的cmd_line,

 87 //保存未改变的comand_line到字符数组static_command_line[] 中。

 88 //保存  boot_command_line到字符数组saved_command_line[]中  

 89 setup_command_line(command_line); 

 90 

 91 unwind_setup();

 92 

 93 //如果没有定义CONFIG_SMP宏,则这个函数为空函数。  

 94 //如果定义了CONFIG_SMP宏,则这个setup_per_cpu_areas()函数给每个CPU分配内存,  

 95 //并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间。  

 96 setup_per_cpu_areas();

 97 

 98 //定义在include/asm-x86/smp.h。  

 99 //如果是SMP环境,则设置boot CPU的一些数据。在引导过程中使用的CPU称为boot CPU  

100 smp_prepare_boot_cpu(); 

101 

102 /* arch-specific boot-cpu hooks */ 

103 /* 进程调度器初始化 */

104 sched_init(); 

105 

106 /* 禁止内核抢占 */  

107 preempt_disable();

108       

109 //设置node 和 zone 数据结构  

110 //内存管理的讲解:http://blog.chinaunix.net/space.php?uid=361890&do=blog&cuid=2146541  

111 build_all_zonelists(NULL); 

112  

113 //初始化page allocation相关结构  

114 page_alloc_init();

115 

116 /* 打印Linux启动命令行参数 */   

117 printk(KERN_NOTICE 'Kernel command line: %s/n', boot_command_line); 

118   

119 //解析内核参数  

120 //对内核参数的解析:http://hi.baidu.com/yuhuntero/blog/item/654a7411e45ce519b8127ba9.html  

121 parse_early_param(); 

122 parse_args('Booting kernel', static_command_line, __start___param, 

123   __stop___param - __start___param, 

124   &unknown_bootoption); 

125 

126 /*

127 * These use large bootmem allocations and must precede

128 * kmem_cache_init()

129 */ 

130 //初始化hash表,以便于从进程的PID获得对应的进程描述指针,按照实际的物理内存初始化pid hash表  

131 //这里涉及到进程管理http://blog.csdn.net/satanwxd/archive/2010/03/27/5422053.aspx  

132 pidhash_init(); 

133 

134 //初始化VFS的两个重要数据结构dcache和inode的缓存。  

135 //http://blog.csdn.net/yunsongice/archive/2011/02/01/6171324.aspx  

136 vfs_caches_init_early(); 

137 

138 //把编译期间,kbuild设置的异常表,也就是__start___ex_table和__stop___ex_table之中的所有元素进行排序  

139 sort_main_extable(); 

140 

141 //初始化中断向量表  

142 //http://blog.csdn.net/yunsongice/archive/2011/02/01/6171325.aspx  

143 trap_init(); 

144 

145 //memory map初始化  

146 //http://blog.csdn.net/huyugv_830913/archive/2010/09/15/5886970.aspx  

147 mm_init(); 

148 

149 /*

150 * Set up the scheduler prior starting any interrupts (such as the

151 * timer interrupt). Full topology setup happens at smp_init()

152 * time - but meanwhile we still have a functioning scheduler.

153 */ 

154 //核心进程调度器初始化,调度器的初始化的优先级要高于任何中断的建立,  

155 //并且初始化进程0,即idle进程,但是并没有设置idle进程的NEED_RESCHED标志,  

156 //所以还会继续完成内核初始化剩下的事情。  

157 //这里仅仅为进程调度程序的执行做准备。  

158 //它所做的具体工作是调用init_bh函数(kernel/softirq.c)把timer,tqueue,immediate三个人物队列加入下半部分的数组  

159 sched_init(); 

160 

161 /*

162 * Disable preemption - early bootup scheduling is extremely

163 * fragile until we cpu_idle() for the first time.

164 */ 

165 //抢占计数器加1   

166  preempt_disable();

167 

168 //检查中断是否打开,如果已经打开,则关闭中断   

169   if (!irqs_disabled()) { 

170   printk(KERN_WARNING 'start_kernel(): bug: interrupts were ' 

171   'enabled *very* early, fixing it/n'); 

172   local_irq_disable(); 

173   } 

174 

175   sort_main_extable(); 

176  /*

177  * trap_init函数完成对系统保留中断向量(异常、非屏蔽中断以及系统调用)              

178  * 的初始化,init_IRQ函数则完成其余中断向量的初始化

179  */

180  trap_init();    

181 

182 //Read-Copy-Update的初始化  

183 //RCU机制是Linux2.6之后提供的一种数据一致性访问的机制,  

184 //从RCU(read-copy-update)的名称上看,我们就能对他的实现机制有一个大概的了解,  

185 //在修改数据的时候,首先需要读取数据,然后生成一个副本,对副本进行修改,  

186 //修改完成之后再将老数据update成新的数据,此所谓RCU。  

187 //http://blog.ednchina.com/tiloog/193361/message.aspx  

188 //http://blogold.chinaunix.net/u1/51562/showart_1341707.html  

189 rcu_init();

190  

191 //初始化IRQ中断和终端描述符。  

192 //初始化系统中支持的最大可能的中断描述结构struct irqdesc变量数组irq_desc[NR_IRQS],  

193 //把每个结构变量irq_desc[n]都初始化为预先定义好的坏中断描述结构变量bad_irq_desc,  

194 //并初始化该中断的链表表头成员结构变量pend  

195 init_IRQ();    

196 

197 /* 初始化hash表,便于从进程的PID获得对应的进程描述符指针 */

198 pidhash_init();

199 

200 //初始化定时器Timer相关的数据结构  

201 //http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html  

202 init_timers();  

203 

204 //对高精度时钟进行初始化  

205 hrtimers_init();

206  

207 //软中断初始化  

208 //http://blogold.chinaunix.net/u1/51562/showart_494363.html  

209 softirq_init(); 

210  

211 //初始化时钟源  

212 timekeeping_init(); 

213  

214 //初始化系统时间,  

215 //检查系统定时器描述结构struct sys_timer全局变量system_timer是否为空,  

216 //如果为空将其指向dummy_gettimeoffset()函数。  

217 //http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html  

218 time_init();  

219 

220 //profile只是内核的一个调试性能的工具,  

221 //这个可以通过menuconfig中的Instrumentation Support->profile打开。  

222 //http://www.linuxdiyf.com/bbs//thread-71446-1-1.html  

223 profile_init();

[1] [2]
关键字:Linux移植  start_kernel函数 引用地址:Linux移植之内核启动过程start_kernel函数简析

上一篇:Linux移植之tag参数列表解析过程分析
下一篇:Linux移植之内核启动过程引导阶段分析

推荐阅读最新更新时间:2024-11-12 17:13

移植较新(Linux3.19)内核至mini2440开发板(一)
下面开始正题 注:内核启动时可能会出现乱码,可以在u-boot下设置如下环境变量:setenv bootargs noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200 saveenv 1.1下载Linux3.19的源代码 从Linux kernel的官方网站可以下载最新的内核代码,我们选择linux-3.19.4.tar.gz这个文件下载。下载后解压至工作目录。进入内核目录,打开Makefile文件,修改如下两行: -ARCH ?= $(SUBARCH) -CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:
[单片机]
S3C2440移植linux3.4.2内核之支持YAFFS文件系统
获取yaffs2源码并给内核打补丁 首先获取yaffs2源码(参考git命令使用详解) cd /work/nfs_root git clone git@github.com:lifeyx/yaffs2.git //若下载出现error:403,可以试试vi /etc/resolv.conf,将nameserver地址改为: 114.114.114.114 将yaffs2源码来配置到内核里(使内核支持yaffs2) vi /work/nfs_root/yaffs2/README-linux 参考上图: /*给内核打补丁*/ cd /work/nfs_root/yaffs2/ ./patch-ker.sh c m /w
[单片机]
S3C6410(Real6410) Linux 6x6矩阵键盘驱动移植
Real6410默认的键盘驱动是gpio键盘驱动,现在要取消gpio键盘,实现矩阵键盘。6410的矩阵键盘驱动对应的是samsung-keypad.c,位于driver/input/keyboard目录,可按照下面步骤进行移植。 1、 在arch/arm/mach-s3c64xx/mach-real6410.c中签加 添加头文件: #include plat/keypad.h 添加struct定义 static uint32_t smdk6410_keymap __initdata = { /* KEY(row, col, keycode) */ /*S00(0,0), S01(0,1),
[单片机]
linux-2.6.30.4移植至2440开发板
一、下载linux-2.6.30.4源码,并解压 ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.30.4.tar.gz tar zxvf linux-2.6.30.4.tar.gz 二、在系统中添加对ARM的支持 $vim Makefile 193#ARCH ?= $(SUBARCH) 194#CROSS_COMPILE ?= 195 ARCH=arm 196 CROSS_COMPILE=arm-linux- 三、修改系统时钟 $vim arch/arm/mach-s3c2440/mach-smdk2440.c 系统的外部时钟为12MHz 16
[单片机]
浅谈分析Arm linux 内核移植及系统初始化的过程
学习嵌入式ARM linux ,主要想必三个方向发展: 1 、嵌入式linux 应用软件开发 2 、linux 内核的剪裁和移植 3 、嵌入式linux 底层驱动的开发 主 要介绍内核移植过程中涉及文件的分布及其用途,以及简单介绍系统的初始化过程。整个arm linux内核的启动可分为三个阶段:第一阶段主要是进行 cpu和体系结构的检查、cpu本身的初始化以及页表的建立等;第二阶段主要是对系统中的一些基础设施进行初始化;最后则是更高层次的初始化,如根设备和 外部设备的初始化。了解系统的初始化过程,有益于更好地移植内核。 1. 内核移植 2. 涉及文件分布介绍 2.1. 内核移植 2.2. 涉及的头文件 /linux-2.6.1
[单片机]
在嵌入式Linux系统(OK6410)中移植Boa 服务器
OK6410的Boa服务器移植: 一 Boa的编译 1. 从 www.boa.org 下载 Boa 服务器的最新版:boa-0.94.13.tar.gz。 2. 解压:tar xzf boa-0.94.13.tar.gz 3. 进入解压后的文件夹 boa-0.94.13内部的 src文件夹,对源文件进行如下修改 1 由于arm-linux-gcc 编译器版本过高,对语法的支持有一些改变,所以需要修改compat.h中的 2 #define TIMEZONE_OFFSET(foo) foo##- tm_gmtoff 3 为: 4 5 #define TIMEZONE_OFFSET(foo) foo- tm_gm
[单片机]
在嵌入式<font color='red'>Linux</font>系统(OK6410)中<font color='red'>移植</font>Boa 服务器
linux-2.6.32在mini2440开发板上移植-移植I2C-EEPROM 驱动
1 在内核中配置I2C 驱动 Linux-2.6.32.2 对S2C2440 的I2C 接口提供了完善的驱动,因此我们只需在内核中配置一下即可使用。 在内核源代码目录执行:make menuconfig,进入内核配置主菜单,依次选择进入如下子菜单: Device Drivers --- * I2C support --- I2C Hardware Bus support --- 如图,我们看到这里已经选择好了“ * S3C2410 I2C Driver”,这里的S3C2410 也可以适用于S3C2440,因为它们的I2C 端口及寄存器定义都是完全相同的。 以上配置所对
[单片机]
<font color='red'>linux</font>-2.6.32在mini2440开发板上<font color='red'>移植</font>-<font color='red'>移植</font>I2C-EEPROM 驱动
基于S3C2440的Linux内核移植和yaffs2文件系统制作--启动系统
第三章 启动系统 将前面两章生成的内核映像文件和根文件系统映像文件下载到mini2440开发板,查看启动信息。我成功移植启动信息如下: VIVI version 0.1.4 ( root@capcross ) (gcc version 2.95.3 20010315 (release)) #0.1.4 Mon Oct 27 10:18:15 CST 2008 MMU table base address = 0x33DFC000 Succeed memory mapping. DIVN_UPLL0 MPLLVal CLKDIVN:5h +------------------------------------------
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved