Linux学习之源码1:入口流程

有地方看到,启动流程是arch/arm/boot/compressed/head.s ----->调用arch/arm/boot/compressed/misc.c的decompress_kernel()
函数解压内核。---->arch/arm/kernel/head-common.S初始化 ---->init/main.c的asmlinkage void __init start_kernel(void)

注意在arch/arm/kernel/smp.c文件中有一个启动多核处理器的函数 void __init smp_prepare_boot_cpu(void),被init/main.c的asmlinkage void __init start_kernel(void)调用

有人说是ARM Linux的启动代码有两处,一处是经过压缩的,一处是没有经过压缩的,压缩的最终还是会调用没有压缩的,没有压缩的入口在arch/arm/kernel/head.S文件中。看起来是这样的,第二种启动流程是在boot/compressed文件夹中,证实是压缩启动流程.

以32位arm为例,在3.9.7内核版本上分析。

1、  压缩启动流程

/arch/arm/boot/compressed/head.S

1)首先定义了一系列的宏,如writeb、loadsp、kputc、kphex、debug_reloc_start、debug_reloc_end;

2)使用r0、r7、r8分别保存r0、r1、r2的值(分别为0、MachineType、ATAG指针),r9是cpsr的值;

3)r2保存cpsr,判断最后2bit是否0,即判断是否user mode,不是的话则设置为svc模式,并把r9中的cpsr保存到spsr;

4)把最终的kernel文件的地址放在r4中,一种方式是通过pc&0xf8000000 + TEXT_OFFSET;一种方式是zreladdr;TEXT_OFFSET = ??

zreladdr在makefile中定义,但是来龙去脉不清楚。

5)跳转到cache_on

6)adr      r0, LC0  //将LC0标签的地址赋给r0,LC0标签处的内容如下图所示

ldmia   r0, {r1, r2, r3, r6, r10, r11, r12}  //将r0地址开始的7个word数据分别保存到r1, r2, r3, r6, r10, r11, r12中

设置堆栈,是LC0开始第8个word的值

/*
   * We might be running at a different address.  We need
   * to fix up various pointers.
   */
  sub r0, r0, r1  @ calculate the delta offset
  add r6, r6, r0  @ _edata
  add r10, r10, r0  @ inflated kernel size location

计算kernel size存放的地址并保存在r10中

LC0:  .word LC0   @ r1
  .word __bss_start  @ r2
  .word _end   @ r3
  .word _edata   @ r6
  .word input_data_end - 4 @ r10 (inflated size location)
  .word _got_start  @ r11
  .word _got_end  @ ip
  .word .L_user_stack_end @ sp
  .size LC0, . - LC0

7)从r10开始的地址依次读取4个字节,拼成32bit存放在r9中,即是kernel size(解压缩的大小)的值;

8)分配空间在堆栈之上,堆栈最大64KB(0x10000),将sp + 0x10000的值赋给r10;

9)初始化dtb的大小为0,并保存在r5中;

10)如果有device trees (dtb) appended to zImage,一些处理,暂时先不分析

截止目前,各寄存器的内容如下图所示:

/*
 *   r0  = delta
 *   r2  = BSS start
 *   r3  = BSS end
 *   r4  = final kernel address
 *   r5  = appended dtb size (still unknown)
 *   r6  = _edata
 *   r7  = architecture ID
 *   r8  = atags/device tree pointer
 *   r9  = size of decompressed image
 *   r10 = end of this image, including  bss/stack/malloc space if non XIP
 *   r11 = GOT start
 *   r12 = GOT end
 *   sp  = stack pointer
 *
 * if there are device trees (dtb) appended to zImage, advance r10 so that the
 * dtb data will get relocated along with the kernel if necessary.
 */

11)判断是否会地址覆盖,

r10 = r10 + 16KB;

r4 > r10则不会覆盖,否则r10 = r4 + r9,r10 < r9则不会覆盖否则就会覆盖

覆盖时的处理这里先不分析

12)orrs   r1, r0, r5   //orr逻辑与,r5为0,当r0及delta也为0,跳转到not_relocated

beq    not_relocated //否则就需要对地址进行跳转,跳转部分我们暂时先不看了

13)清零bss段,从r2开始的地址到r3开始的地址

14)解压缩C代码环境准备及解压缩

decompress_kernel(

unsigned long output_start,  //(r4中的值,移送到r0)

unsigned long free_mem_ptr_p, //(sp的值,移送到r1)

unsigned long free_mem_ptr_end_p, //(sp值+64KB,移送到r2)

int arch_id //(r7中的值,移送到r3)

);

分别准备4个参数,保存在r0~r3中,然后调用decompress_kernel函数

15)刷cache、关闭cache,分别调用cache_clean_flush、cache_off

16)再把r7、r8中的architecture number、atags pointer恢复到r1、r2中

跳转到__enter_kernel标签,在这里,将r0恢复到0,r4中的解压缩内核地址移送到pc中,跳转到r4中,完成从压缩内核流程向非压缩内核流程转换。

2、非压缩启动流程

代码入口是在arch\arm\kernel\head.s,然后跳转到main.c中的start_kernel。

确保SVC模式和中断关闭

|

获取cpuid

|

判断ATAG,貌似只判断了ATAG_CORE

|

__create_page_tables,创建页表

|

将__mmap_switched地址保存在r13中

|

跳转到__enable_mmu,

在__turn_mmu_on的最后跳转到r13中,即跳转到__mmap_switched

|

在__mmap_switched的最后 b start_kernel

这段GCC汇编看的好痛苦,有些在as文档中都找不到,也不知道是什么意思,所以还只是个大概。

http://linux.chinaunix.net/bbs/thread-1021226-1-1.html中有详细的描述这段汇编。

Linux学习之源码1:入口流程,布布扣,bubuko.com

时间: 2024-08-06 18:48:22

Linux学习之源码1:入口流程的相关文章

Linux学习之源码2:start_kernel流程

一.X86的流程可以参考http://www.kerneltravel.net/kernel-book/第十三章%20启动系统/13.5.htm 二.arm的流程,在http://www.cnblogs.com/gangsaleisi/archive/2013/01/09/2851734.html基础上进行分析. 并且是在3.9.7版本上进行分析的,差别不是太大. 1.lockdep_init():lockdep哈希表初始化,lockdep是linux内核的一个调试模块,用来检查内核互斥机制尤其

Linux学习之源码包安装与脚本安装(十八)

Linux学习之源码包安装与脚本安装 目录 源码包与RPM包的区别 源码包安装 脚本安装 源码包与RPM包的区别 1.区别 安装之前的区别:概念上的区别 安装之后的区别:安装位置不同 源码包: 开源的 安装更慢,更容易报错 安装更自由 安装完后效率更高 RPM包: 经过编译,能看到源代码 安装更快,报错容易解决 2.RPM包安装位置与源码包安装位置 RPM包安装位置: 源码包安装位置: 安装在指定位置当中,一般是/usr/local/软件名/  3.安装位置不同带来的影响 RPM包安装的服务可以

Linux学习日记——源码编译Apache

[本文为笔者在学习Linux 下的软件安装时,尝试使用源码安装Apache 的过程,事后进行一个小小的总结,发现错误望指正.] 一.典型的源码编译安装软件的过程包括以下3步: 1) 运行 configure 命令,并结合必要的参数以生成Makefile :(读者可以自行百度Makefile 相关知识) 2) 运行 make 命令生成各类模块和主程序: 3) 运行 make install 命令将必要的文件复制到安装目录中. (以上过程都在对应软件安装包的根目录中进行) 二.安装过程 Linux

linux学习笔记——源码编译安装Mysql

#######Redhat6.5源码编译安装Mysql########实验环境:1.IP:172.25.8.32.磁盘要大于20G先添加一块大于20G的磁盘fdisk /dev/vdb        ##得到/dev/vdb1 8e linuxpvcreate /dev/vdb1    ##把物理分区做成物理卷vgextend vg_server1 /dev/vdb1    ##把新建立的/dev/vdb1添加到vg_server1中lvextend -L 20G /dev/vg_server1

Linux编译安装源码包的流程

本文参考:http://www.linuxfromscratch.org/lfs/view/7.10-rc1/chapter05/generalinstructions.html 流程: 1.  下载并解压源码包2.  运行:configure3.  编译:make4.  安装:make install 编译时需要注意一个原则:不要在解压的包中直接执行./configure.make.make install等命令,需要在源码目录下另外新建一个目录,在新建的目录中执行以上命令. 例子:这里以源码

linux学习笔记——源码编译安装PHP

#######Redhat6.5源码编译安装php########实验环境:IP:172.25.8.3(已经安装了nginx.mysql服务并能正常使用) 实验内容:1.安装包php-5.6.20.tar.bz2re2c-0.13.5-1.el6.x86_64.rpm   ##PHP的词法解释器re2clibmcrypt-2.5.8-9.el6.x86_64.rpm   ##提供mcrypt,mcrypt是php中重要的加密支持扩展库libmcrypt-devel-2.5.8-9.el6.x86

数据库学习之--Linux下Mysql源码包安装

数据库学习之--Linux下Mysql源码包安装 系统环境: 操作系统:RedHat EL6 DB Soft:  Mysql 5.6.4-m7     Mysql 在linux下的安装方式有两种版本,一种为Binary(二进制),另外一种为Source(源码包),本文为Source Install方式. 1.安装前的准备 解压安装包 [[email protected] ~]$ ls mysql-5.6.4-m7  mysql-5.6.4-m7.tar.gz  mysql-5.6.4-m7.ta

linux卸载一个源码包安装的软件的流程

完全卸载memcached的方法(CentOS) 我的大内存vps(centos系统)曾经安装过memcached,想给论坛提速,实际上不但没有明显效果,反倒耗费内存,看着碍眼,于是想卸载,于是网上各种搜索+自己实践,搞出一个傻瓜方案来: 1.结束memcached进程# killall memcached 2.删除memcached目录及文件# rm -rf /usr/local/memcached# rm -f /etc/rc.d/init.d/memcached 3.关闭memcached

一起学习redis源码

redis的一些介绍,麻烦阅读前面的几篇文章,想对redis的详细实现有所了解,强力推荐<redis设计与实现>(不仅仅从作者那儿学习到redis的实现,还有项目的管理.思想等,作者可能比你我都年轻欧).如果阅读了上面的文档,激起你对redis的强力好奇,那么就只能阅读源码了.不管是在校学生,还是已工作的,redis的代码都值得阅读.我们可以了解如何编写一个工程可用软件,可以学习一些开源常用软件,通过redis丰富的数据结构,可以熟悉大学学习的那点儿数据结构,可以了解如何实现一个自己高效的网络