kernel(二)源码浅析
title: kernel(二)源码浅析
tags: linux
date: 2018-11-08 18:02:34
kernel(二)源码浅析
建立工程
移除所有Arch,添加Arch/arm 下除了 Mach_xxx 开头的,Mach_xxx 表示机器型号,添加2410,2440,剔除 Plat_xxx,加入plat-s3c24xx
Arch/arm/ boot
common
configs
kernel
lib mach-s3c2410
mach-s3c2440
plat-s3c24xx mm
nwfpe
oprofile
tools
vfp
移除include目录,先排除所有Asm相关,只加入asm-arm顶层文件以及2440相关的如下
include/
排除所有 asm-xxx include/asm-arm/下添加 所有顶层以及以下目录
arch-s3c2410
hardware
mach
plat-s3c24xx
启动简析
uboot启动通过theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
中的第二个参数是机器ID,内核通过比对机器ID判断是否支持启动.gd->bd->bi_arch_number = MACH_TYPE_S3C2440;
linux会这么做:
- 处理uboot传入的参数
- 挂接根文件系统
- 最终目的:运行应用程序(在根文件系统上)
内核跳转之前,Uboot设置内核的启动参数.内核的参数是按照tag
组织的.也就是在某个地址(0x30000100,在100ask24x0.c中定义),按照某种格式存储,这种格式具体为【size....tagid....tag值】
head.s
我们发现在arch\arm\boot\compressed
也存在一个head.S的文件,有些内核编译出来比较大,他会以压缩的形式存在也就是包含了自解压的代码,这个文件就是讲压缩的文件解压,在这里不做分析。我们的入口为arch\arm\kernel\head.S
入口点
链接脚本有写,也就是说_stext
段为最早的入口点,搜索下head.S中的入口点.text.head
.text.head : { #先放所有文件的 .text.head 段
_stext = .;
_sinittext = .;
*(.text.head)
}
查询处理器
查看是否支持__lookup_processor_type
.section ".text.head", "ax"
.type stext, %function
ENTRY(stext)
msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
@ and irqs disabled
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
movs r10, r5 @ invalid processor (r5=0)?
beq __error_p @ yes, error 'p'
bl __lookup_machine_type @ r5=machinfo
movs r8, r5 @ invalid machine (r5=0)?
beq __error_a @ yes, error 'a'
bl __create_page_tables
内核能够支持哪些处理器,是在编译内核时定义下来的。内核启动时去读寄存器:获取 ID。看内核是否可以支持这个处理器。若能支持则继续运行,不支持则跳到 _error_p
中去,这是个死循环
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
movs r10, r5 @ invalid processor (r5=0)?
beq __error_p @ yes, error 'p'
查询机器ID
如果不支持这个机器ID则跳转到__error_a
,这也是死循环
bl __lookup_machine_type @ r5=machinfo
movs r8, r5 @ invalid machine (r5=0)?
beq __error_a @ yes, error 'a'
机器ID是存在R1
的,因为theKernel (0, bd->bi_arch_number, bd->bi_boot_params)
3: .long .
.long __arch_info_begin
.long __arch_info_end
@ __arch_info_begin 和 __arch_info_end 是在链接脚本中定义的
@ __arch_info_begin = .;
@ *(.arch.info.init)
@ __arch_info_end = .;
/*
* Lookup machine architecture in the linker-build list of architectures.
* Note that we can't use the absolute addresses for the __arch_info
* lists since we aren't running with the MMU on (and therefore, we are
* not in the correct address space). We have to calculate the offset.
*
* r1 = machine architecture number
* Returns:
* r3, r4, r6 corrupted
* r5 = mach_info pointer in physical address space
*/
.type __lookup_machine_type, %function
__lookup_machine_type:
adr r3, 3b @ r3= address of 3b,这个时候mmu还没有启动,是物理地址
ldmia r3, {r4, r5, r6} @ r4=.,r5=__arch_info_begin,r6=__arch_info_end
@ 这个.代表了3这个标号的虚拟地址
sub r3, r3, r4 @ get offset between virt&phys 虚拟地址与物理地址的偏差
@将r5,r6转换为实际的物理地址
add r5, r5, r3 @ convert virt addresses to
add r6, r6, r3 @ physical address space
1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type
teq r3, r1 @ matches loader number?
beq 2f @ found
add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
cmp r5, r6
blo 1b
mov r5, #0 @ unknown machine
2: mov pc, lr
首先是将虚拟地址转换为物理地址,因为这个时候UBOOT 启动内核时,MMU 还没启动,r3 这是实际存在的地址
3: .long .
.long __arch_info_begin
.long __arch_info_end
adr r3, 3b
接下来的r4, r5, r6
都是虚拟地址了. .
代表虚拟地址。是标号为 3
的指令的虚拟地址.可以通过r3
与.
(虚拟地址)来计算偏差.
sub r3, r3, r4 @ r3=r3-r4,也就r3=物理地址-虚拟地址的偏差
实际物理地址=虚拟地址+r3即可
add r5, r5, r3 @ convert virt addresses to
add r6, r6, r3 @ physical address space
查看下__arch_info_begin和__arch_info_end
具体是什么,这个是在链接脚本定义如下的,也就是代表了一个段
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
.arch.info.init
在arch.h
中有定义 ,定义某个结构体(machine_desc
)的段属性
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
有如下应用
MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
展开看看
static const struct machine_desc __mach_desc_S3C2440
__used
__attribute__((__section__(".arch.info.init"))) = {
.nr = MACH_TYPE_S3C2440,
.name = SMDK2440,
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
};
查看下machine_desc 这个结构体内容,可以发现支持多少单板,就有多少这个宏的使用
struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head-armv.S
*/
unsigned int nr; /* architecture number */
unsigned int phys_io; /* start of physical io */
unsigned int io_pg_offst; /* byte offset for io
* page tabe entry */
const char *name; /* architecture name */
unsigned long boot_params; /* tagged list */
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned int reserve_lp0 :1; /* never has lp0 */
unsigned int reserve_lp1 :1; /* never has lp1 */
unsigned int reserve_lp2 :1; /* never has lp2 */
unsigned int soft_reboot :1; /* soft reboot */
void (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
};
接下去就是从这个结构体读取第一个参数nr
也就是ID来逐个比较了.这个在内核中定义与uboot定义是一致的
#define MACH_TYPE_S3C2440 362
启动MMU
bl __create_page_tables @创建页表
ldr r13, __switch_data @ address to jump to after,这是使能mmu后的跳转地址
@ mmu has been enabled
adr lr, __enable_mmu @ return (PIC) address 使能mmu
add pc, r10, #PROCINFO_INITFUNC
@__enable_mmu 中会调用 __turn_mmu_on,最后 mov pc, r13
b __turn_mmu_on
.align 5
.type __turn_mmu_on, %function
__turn_mmu_on:
mov r0, r0
mcr p15, 0, r0, c1, c0, 0 @ write control reg
mrc p15, 0, r3, c0, c0, 0 @ read id reg
mov r3, r3
mov r3, r3
mov pc, r13 @这个是关键,pc最后=r13=__switch_data
其他操作
复制数据段,清bss段等操作
start_kernel
启动mmu后会跳转到__switch_data
,如何跳到 __switch_data
,在 __enable_mmu
中会调用 __turn_mmu_on
这个函数最后 mov pc, r13
,在调用__enable_mmu
前是先赋值的ldr r13, __switch_data
ldr r13, __switch_data @ address to jump to after
....
b start_kernel
注意 这是内核的第一个 C 函数,接下来要处理UBOOT 传输的第三个启动参数bd->bi_boot_params
.这个文件在init/main.c
,在以下函数处理参数
setup_arch(&command_line);
setup_command_line(command_line);
一览流程如下
start_kernel
setup_arch //解析uboot传入的参数,只是先存起来字符串
setup_command_line //只是先存起来字符串
parse_args
do_early_param
从__setup_start 中调用early函数
unknown_bootoption
obsolete_checksetup
从__setup_start 中调用非early函数 段属性
rest_init
kernel_init
prepare_namespace
mount_root //根文件系统,
init_post // 执行应用程序
setup_arch(解析tag)
这里是先查找mdesc
这个结构体,这个结构体在上面分析机器ID的时候已经发现他保存了一系列参数.boot_params
就是uboot存放参数的地址.然后在parse_tags(tags)
处理具体的tag
if (mdesc->boot_params)
tags = phys_to_virt(mdesc->boot_params);
//在上面定义机器id结构体的时候,.boot_params = S3C2410_SDRAM_PA + 0x100,=0x30000100
#define S3C2410_CS6 (0x30000000)
#define S3C2410_SDRAM_PA (S3C2410_CS6)
//我们在uboot的时候存储参数的地址也是这个
board_init -----gd->bd->bi_boot_params = 0x30000100;
然后开始处理tags
static const struct machine_desc __mach_desc_S3C2440
__used
__attribute__((__section__(".arch.info.init"))) = {
.nr = MACH_TYPE_S3C2440,
.name = SMDK2440,
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
};
setup_command_line
所谓命令行,就是uboot设置的bootargs
,linux通过getenv("bootargx")
获取参数,如果没有设置这个参数,内部有一个默认参数. 这里只是将命令行复制到指定的数组,并没有处理
char *from = default_command_line; //这是默认的命令行参数
static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
#define CONFIG_CMDLINE "root=/dev/hda1 ro init=/bin/bash console=ttySAC0"
memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
parse_cmdline(cmdline_p, from); //
// cmdline_p 是 传递的参数,用作拷贝
// from 是默认参数
挂载根文件系统
创建一个线程,可以理解为运行程序kernel_init
rest_init
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
kernel_init
>prepare_namespace
>mount_root 挂在根文件系统
>init_post(); 执行应用程序
处理命令行
uboot设置命令tag,多了参数commandline
,源自环境变量bootargs
查看下环境变量bootargs
,使用print
查看,也可搜索下代码
"bootargs=" CONFIG_BOOTARGS "\0"
//include/configs/100ask24x0.h
#define CONFIG_BOOTARGS "noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"
root=/dev/mtdblock3
表示根文件系统从第四个FLASH分区开始(从0开始计数)可以往上看分区空间init=/linuxrc
指示第一个应用程序console=ttySAC0
,内核打印信息从串口0 打印
我们需要知道ROOT_DEV
是什么,可以看到在函数prepare_namespace
中有saved_root_name
存储这个这个数组.
if (saved_root_name[0]) {
root_device_name = saved_root_name;
if (!strncmp(root_device_name, "mtd", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name);
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
搜索下saved_root_name
,root_dev_setup
对齐赋值,再继续查找函数的引用,只有一个宏使用了它
static int __init root_dev_setup(char *line)
{
strlcpy(saved_root_name, line, sizeof(saved_root_name));
return 1;
}
解析下这个宏__setup("root=", root_dev_setup);
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
#define __setup_param(str, unique_id, fn, early) \
static char __setup_str_##unique_id[] __initdata = str; \
static struct obs_kernel_param __setup_##unique_id \
__attribute_used__ \
__attribute__((__section__(".init.setup"))) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }
static char __setup_str_root_dev_setup[] __initdata = "root=";
static struct obs_kernel_param __setup_root_dev_setup
__attribute_used__
__attribute__((__section__(".init.setup")))
__attribute__((aligned((sizeof(long)))))
={
__setup_str_root_dev_setup,root_dev_setup,root_dev_setup,0
}
这个结构体的原型如下
struct obs_kernel_param
{
const char *str;
int (*setup_func)(char *);
int early;
};
最终大概分析一下也就是定义了一个char数组和一个有特殊段属性.init.setup
的结构体.注意这里的early
是0.这个段属性肯定是在lds中定义,搜索下这个段的起始和结束地址的调用情况.
__setup_start = .;
*(.init.setup)
__setup_end = .;
obsolete_checksetup
do_early_param
static int __init do_early_param(char *param, char *val)
{
struct obs_kernel_param *p;
for (p = __setup_start; p < __setup_end; p++) {
if (p->early && strcmp(param, p->str) == 0) {
if (p->setup_func(val) != 0)
printk(KERN_WARNING
"Malformed early option '%s'\n", param);
}
}
/* We accept everything at this stage. */
return 0;
}
static int __init obsolete_checksetup(char *line)
{
struct obs_kernel_param *p;
int had_early_param = 0;
p = __setup_start;
do {
int n = strlen(p->str);
if (!strncmp(line, p->str, n)) {
if (p->early) {
/* Already done in parse_early_param?
* (Needs exact match on param part).
* Keep iterating, as we can have early
* params and __setups of same names 8( */
if (line[n] == '\0' || line[n] == '=')
had_early_param = 1;
} else if (!p->setup_func) {
printk(KERN_WARNING "Parameter %s is obsolete,"
" ignored\n", p->str);
return 1;
} else if (p->setup_func(line + n))
return 1;
}
p++;
} while (p < __setup_end);
return had_early_param;
}
可以看出obsolete_checksetup
先判断这个结构的early
属性,为0则执行setup_func
方法,这符合我们的这个宏__setup("root=", root_dev_setup);
结论:挂接根文件系统的参数是由命令行给出的,内核函数去分析这个命令行,去赋值ROOT_DEV
分区
分区表是没有的,是代码里面写死的,我们可以启动内核的时候发现有以下输出
Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":
0x00000000-0x00040000 : "bootloader"
0x00040000-0x00060000 : "params"
0x00060000-0x00260000 : "kernel"
0x00260000-0x10000000 : "root"
可以搜索这个"bootloader"
发现在arm/plat-s3c24xx/common-smdk.c
下面定义
static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "bootloader",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "params",
.offset = MTDPART_OFS_APPEND,
.size = 0x00020000,
},
[2] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x00200000,
},
[3] = {
.name = "root",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
MTDPART_OFS_APPEND
这个 offset 意思是紧接着上面一个分区的意思
kernel(二)源码浅析的更多相关文章
- Android源码浅析(二)——Ubuntu Root,Git,VMware Tools,安装输入法,主题美化,Dock,安装JDK和配置环境
Android源码浅析(二)--Ubuntu Root,Git,VMware Tools,安装输入法,主题美化,Dock,安装JDK和配置环境 接着上篇,上片主要是介绍了一些安装工具的小知识点Andr ...
- ReentrantLock和condition源码浅析(二)
转载请注明出处... 接着上一篇的ReentrantLock和condition源码浅析(一),这篇围绕着condition 一.condition的介绍 在这里为了作对比,引入Object类的两个方 ...
- Android源码浅析(三)——Android AOSP 5.1.1源码的同步sync和编译make,搭建Samba服务器进行更便捷的烧录刷机
Android源码浅析(三)--Android AOSP 5.1.1源码的同步sync和编译make,搭建Samba服务器进行更便捷的烧录刷机 最近比较忙,而且又要维护自己的博客,视频和公众号,也就没 ...
- Android 手势识别类 ( 三 ) GestureDetector 源码浅析
前言:上 篇介绍了提供手势绘制的视图平台GestureOverlayView,但是在视图平台上绘制出的手势,是需要存储以及在必要的利用时加载取出手势.所 以,用户绘制出的一个完整的手势是需要一定的代码 ...
- Android源码浅析(四)——我在Android开发中常用到的adb命令,Linux命令,源码编译命令
Android源码浅析(四)--我在Android开发中常用到的adb命令,Linux命令,源码编译命令 我自己平时开发的时候积累的一些命令,希望对你有所帮助 adb是什么?: adb的全称为Andr ...
- Android源码浅析(一)——VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置
Android源码浅析(一)--VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置 最近地方工作,就是接触源码的东西了,所以好东西还是要分享,系列开了这 ...
- 我对java String的理解 及 源码浅析
摘要: 摘要: 原创出处: http://www.cnblogs.com/Alandre/ 泥沙砖瓦浆木匠 希望转载,保留摘要,谢谢! 每天起床告诉自己,自己的目标是 ”技术 + 英语 还有生活“! ...
- MySQL多版本并发控制机制(MVCC)-源码浅析
MySQL多版本并发控制机制(MVCC)-源码浅析 前言 作为一个数据库爱好者,自己动手写过简单的SQL解析器以及存储引擎,但感觉还是不够过瘾.<<事务处理-概念与技术>>诚然 ...
- HashMap的源码浅析
一.HashMap 的数据结构 Java7 及之前主要是"数组+链表",到了 Java8 之后,就变成了"数组+链表+红黑树". 二.Java7 源码浅析: 在 ...
随机推荐
- 解决vscode egg调试出现: this socket has been ended by other party【转】
如果是最新的1.22 方案一 回退版本到1.21.1 https://code.visualstudio.com/updates/v1_21 方案二 退而求其次, 更改debug配置, 待官方或egg ...
- #186 path(容斥原理+状压dp+NTT)
首先只有一份图时显然可以状压dp,即f[S][i]表示S子集的哈密顿路以i为终点的方案数,枚举下个点转移. 考虑容斥,我们枚举至少有多少条原图中存在的边(即不合法边)被选进了哈密顿路,统计出这个情况下 ...
- Django+Xadmin打造在线教育系统(四)
完成授课机构的功能 模板继承 在templates目录下,新建base.html,剪切org-list.html内容到里面 编写org-list.html内容 继承base.html,将里面的面包屑和 ...
- ionic更改端口号
—— 重新指定端口号为8888 serve [options] ............................... 启动本地服务器进行开发测试 dev/testing [--console ...
- 阿里云上,Ubuntu下配置Nginx,在tomcat中加了https协议就不可以了
问题 阿里云上,Ubuntu服务器,本来部署的是tomcat,并且使用了https 协议.后来为了静态资源分离集成了 nginx,nginx代理跳转到 tomcat.刚开始直接访问http 网址发现, ...
- MT【261】左准线
设双曲线$x^2-\dfrac{y^2}{3}=1$的左右焦点为$F_1,F_2$, 直线$l$ 过$F_2$且与双曲线交于$A,B$两点.若$l$的斜率存在,且$(\overrightarrow{F ...
- 「HDU6158」 The Designer(圆的反演)
题目链接多校8-1009 HDU - 6158 The Designer 题意 T(<=1200)组,如图在半径R1.R2相内切的圆的差集位置依次绘制1,2,3,到n号圆,求面积之和(n< ...
- NOIP经典基础模板总结
date: 20180820 spj: 距离NOIP还有81天 目录 STL模板: priority_queue 的用法:重载<,struct cmpqueue 的用法 stack 的用法vec ...
- html图像、绝对路径和相对路径,链接
html图像 <img>标签可以在网页上插入一张图片,它是独立使用的标签,通过"src"属性定义图片的地址,通过"alt"属性定义图片加载失败时显示 ...
- SP8791 DYNALCA - Dynamic LCA 解题报告
SP8791 DYNALCA - Dynamic LCA 有一个森林最初由 \(n (1 \le n \le 100000)\) 个互不相连的点构成 你需要处理以下操作: link A B:添加从顶点 ...