一起分析Linux系统设计思想——03内核启动流程分析(七)

在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的垃圾信息,将知识体系化,以短平快的方式直达问题本质,把大家从大海捞针的痛苦中解脱出来。

10 本节新增汇编指令汇总(接上篇)

10.1 arm汇编指令执行条件码

条件码助记符标志位说明
EQ (EQual)Z=1相等
NE (Not Equal)Z=0不相等
HI (HIgh)C=1,Z=0无符号数大于
CS/HS (Carry Set/High or Same)C=1无符号数大于或等于
CC/LO (Carry Clear/LOwer)C=0无符号数小于
LS (Lower or Same)C=0,Z=1无符号数小于或等于
GT (Greater Than)Z=0,N=V有符号数大于
GE (Greater or Equal)N=V有符号数大于或等于
LT (Less Than)N!=V有符号数小于
LE (Less or Equal)Z=1,N!=V有符号数小于或等于
MI (MInus)N=1负数
PL (PLus)N=0正数或零
VS (oVerflow Set)V=1溢出
VC (oVerflow Clear)V=0没有溢出
AL (ALl)任何无条件执行(默认)
NV (NeVer)任何从不执行

10.2 arm通用寄存器PCS

所谓PCS(Procedure Call Standard for Arm architecture),就是在过程调用中,寄存器的特殊用途。

如下表:

通用寄存器名称特殊用途名称说明
r15pc (Program Counter)程序计数器,用于控制程序流程
r14lr (Linker Register)链接寄存器,用于函数返回
r13sp (Stack Pointer)栈顶指针,用于指示栈顶位置
r12ip (Intra-Procedure-call scratch register)用来暂存sp压栈前的值,以便在压栈过程中压入
r11fp (Frame Pointer)栈帧指针,用于追溯函数调用关系或优化代码

11 __switch_data剖析

11.1 代码详细分析

/*
 *  linux/arch/arm/kernel/head-common.S
 *
 *  Copyright (C) 1994-2002 Russell King
 *  Copyright (c) 2003 ARM Limited
 *  All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

	.type	__switch_data, %object @ 声明__switch_data是一个变量(可以理解为结构体)
__switch_data:
	.long	__mmap_switched     @ 第一个成员是函数指针
	                            @ 这里特别做一个说明,r13中存储的不是__switch_data,
	                            @ 而是mem(__switch_data,4),也就是__mmap_switched。
	                            
	.long	__data_loc			@ r4 data_location,数据段存储地址(由链接器定义)
	.long	__data_start			@ r5 .data段起始(链接)地址(由链接器定义)
	.long	__bss_start			@ r6,.bss段起始(链接)地址/.data段结束地址(由链接器定义)
	.long	_end				    @ r7,.bss段结束(链接)地址/内核结束地址(由链接器定义)
	.long	processor_id	        @ r4, unsigned int processor_id;(定义在/arc/arm/kernel/setup.c中)
	.long	__machine_arch_type	@ r5, unsigned int __machine_arch_type;(定义在/arc/arm/kernel/setup.c中)
	.long	cr_alignment			@ r6,定义在/arch/arm/kernel/entry-armv.S中:
                                @        .globl  cr_alignment
                                @        .globl  cr_no_alignment
                                @    cr_alignment:
                                @        .space  4
                                @    cr_no_alignment:
                                @        .space  4
                                        
	.long	init_thread_union + THREAD_START_SP @ sp

/*
 * The following fragment of code is executed with the MMU on in MMU mode,
 * and uses absolute addresses(你看,这里出现了绝对地址); 
 * this is not position independent.(这里出现了位置有关码)
 * 因此,概念搞清楚再看代码才更通透,没看过上一篇文章的建议看一看里面的概念介绍
 * 
 *  当前寄存器占用情况如下:
 *  r0 = c1 parameters (用来配置控制寄存器的参数) 
 *  r4 = pgtbl (page table 的物理基地址) 
 *  r8 = machine info (struct machine_desc的基地址) 
 *  r9 = cpu id (processor ID,通过cp15获得的cpu id) 
 *  r10 = procinfo (struct proc_info_list的基地址)
 *
 *  输入参数如下:
 *  r0  = cp#15 control register
 *  r1  = machine ID
 *  r9  = processor ID
 */
	.type	__mmap_switched, %function
__mmap_switched:
	adr	r3, __switch_data + 4 @ 将r3指向__data_loc指针变量所在地址

	ldmia	r3!, {r4, r5, r6, r7} @ r4 = mem[r3,4] = __data_loc;
	                            @ r5 = mem[r3+4*1,4] = __data_start;
	                            @ r6 = mem[r3+4*2,4] = __bss_start;
	                            @ r7 = mem[r3+4*3,4] = _end;
	                            @ r3 = r3 + 4*4。
	cmp	r4, r5				@ Copy data segment if needed,
	                        @ 查看数据段链接地址和存储地址是否相同
	                        @ 如果不相同则需要将数据段从存储地址处拷贝到链接地址处
	                        
1:	cmpne	r5, r6          @ 该行代码有两个作用,一个是控制第一次是否拷贝;另一个是
                           @ 控制拷贝结束条件(r5是否最终指向了__bss_start)。
                           
	ldrne	fp, [r4], #4   @ 这两行就是从__data_loc拷贝到__data_start。还有一个点,
	strne	fp, [r5], #4   @ 这里为啥要用fp?其实fp在这里就是当普通寄存器使用的,因为其他寄存器都被占用了。
	bne	1b

	mov	fp, #0				@ Clear BSS (and zero fp),同时清除了fp
1:	cmp	r6, r7              @ 循环清除.bss段
	strcc	fp, [r6],#4
	bcc	1b

	ldmia	r3, {r4, r5, r6, sp} @ 如法炮制,使用r4,r5,r6分别指向processor_id,__machine_arch_type
	                           @ 和cr_alignment。需要特别说明的是,该步也设置了堆栈指针sp,
	                           @ sp = init_thread_union + THREAD_START_SP
	str	r9, [r4]		@ Save processor ID(存入内存变量processor_id)
	str	r1, [r5]			@ Save machine type(存入内存变量__machine_arch_type)
	bic	r4, r0, #CR_A			@ Clear 'A' bit
	stmia	r6, {r0, r4}			@ Save control register values,
	                            @ 将r0和r4的值分别存入内存变量cr_alignment和cr_no_alignment中
	                            
	b	start_kernel      @ 跳转到C语言入口start_kernel执行,一去不复返。

11.2 栈指针设置说明

下面详细分析下栈指针的设置。

sp = init_thread_union + THREAD_START_SP

其中,init_thread_union的定义如下:

/* /arch/arm/kernel/init_task.c */
/*
 * Initial thread structure.
 *
 * We need to make sure that this is 8192-byte aligned(一定要保证8KB对齐)due to the
 * way process stacks are handled. This is done by making sure
 * the linker maps this in the .text segment right after head.S,
 * and making head.S ensure the proper alignment.
 *
 * The things we do for performance..
 */
union thread_union init_thread_union
	__attribute__((__section__(".data.init_task"))) =
		{ INIT_THREAD_INFO(init_task) };

/* /include/linux/sched.h */
union thread_union {
	struct thread_info thread_info;
	unsigned long stack[THREAD_SIZE/sizeof(long)];
};

其中,THREAD_START_SP的定义如下:

/* /include/asm-arm/thread_info.h */
#define THREAD_SIZE		8192
#define THREAD_START_SP		(THREAD_SIZE - 8)

我们综合以上信息,可以知道,栈空间其实是 “ 寄居 ”在init_task的进程描述符表中的,而且是寄居在高地址处(因为是满减栈)。有一个细节是sp并没有指向8KB的边界,而是留出了8Byte的空间,我个人认为是出于安全考虑,留出的 安全带 ,大家有什么好的想法可以留言探讨。

我们再来看看链接脚本中是如何保证init_thread_union的首地址是8KB对齐的。至于为什么要8KB对齐,就涉及到内存管理了,我们后续再讨论。

/* /arch/arm/kernel/vmlinux.lds */
...
 . = ALIGN(8192); /*这里保证了8KB对齐*/
 __data_loc = .;


 .data : AT(__data_loc) {
  __data_start = .; /* address in memory */

  /*
		 * first, the init task union, aligned
		 * to an 8192 byte boundary.
		 */
  *(.data.init_task)
 ...

<完>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

穿越临界点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值