当前位置: 首页 > news >正文

泰安新闻频道在线直播/seo营销推广公司

泰安新闻频道在线直播,seo营销推广公司,搜关键词可以搜到的网站怎么做,做视频的网站那几个盈利了在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的垃圾信息,将知识体系化,以…

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

文章目录

    • 1 内核启动总体流程
      • 1.1 核心目的
      • 1.2 总体流程
    • 2 stext剖析
      • 2.1 流程分析
      • 2.2 __cpu_flush函数

1 内核启动总体流程

分析内核启动流程时一定要 抓大放小 ,千万不要上来就深入到每一个函数,每一个细节。如果这样就会只见树木不见森林,逐渐迷路。正确的做法是先掌握根本目的和大体流程,然后再逐个击破。

1.1 核心目的

无论看多少代码或写多少代码,一定不要忘记最终的目的。Linux内核启动的最终目的就是——运行应用程序。

根据这个最终目的再去理解内核启动流程中的关键步骤就容易多了。比如,运行应用程序首先要找到应用程序存储的地方吧,这就需要挂载根文件系统。再比如,运行应用程序需要加载到内存中运行吧,这就需要初始化页表并打开MMU。

1.2 总体流程

首先我们梳理一下Linux内核启动的总体流程,在脑海中建立起关键步骤的 枝干 ,后续我会根据需要挑拣 枝叶 再进行详细分析。

先上图:

在这里插入图片描述

我向来不赞成死记硬背,凡事都遵循 因果报应 ,计算机也不例外,只不过这里叫 逻辑 。只要你想明白了,流程自然就应该是这样的。比如,bootloader给内核传递了两类参数,一个是机器ID,一个是启动参数。那既然bootloader给内核传递了这两个参数,那内核就肯定会处理对吧,那就必然有对机器ID的处理步骤和对启动参数的解析。进一步的,我们还可以 刨根问底 ,bootloader为什么要传递这两类参数,为什么不是其他的?这两类参数中机器ID是必须要传递的,那咱就先分析这个为啥必须传递。其实,原因也很简单,它们之前其实是一体的,后来才分工的,可以将bootloader理解成内核的 开道先锋 ,所以,bootloader开的道必须是内核想走的路才对,这里的路就是硬件啦。每一款硬件是用机器ID来进行区分。因此机器ID必须由bootloader传递给内核,这是他们的 接头暗号

2 stext剖析

该节用到的汇编指令汇总如下表:

指令功能说明
msrMove register to PSR status/flags操纵PSR寄存器时才会使用该指令
mrcMove from coprocessor register to CPU register读协处理器时才会使用该指令
mov(s)Move register or constant加s代表会影响标志位
ldrLoad register or constant加载绝对地址(链接地址)
adrAddress of register or constant加载小范围地址(pc+offset)
blBranch with link该指令会在跳转到标号之前保存当前pc(r15)指针内容到lr(r14)寄存器中,用于函数调用返回使用
beqBranch该指令中eq为执行b指令的条件——CPSR中的Z=1
addaddsum = add1 + add2

2.1 流程分析

stext是Linux内核的第一个入口函数,代码都是位置无关码(PIC)。在Linux内核启动之前bootloader已经将0放到了r0中,将机器ID放到了r1中。

Tips:在分析内核启动代码前了解当前寄存器中的值和含义是十分必要的,因为汇编语言的特点就是充分利用了 分时复用 的思想,不同的时刻,寄存器中的值是不同的,有着不同的含义。汇编语言比较繁琐的一点就在于这里——一句话不会在一行说完,想理解当前行的意思,必须查看上下文,综合去理解分析。

/** Kernel startup entry point.* ---------------------------** This is normally called from the decompressor code.  The requirements* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,* r1 = machine nr.** This code is mostly position independent, so if you link the kernel at* 0xc0008000, you call this at __pa(0xc0008000).** See linux/arch/arm/tools/mach-types for the complete list of machine* numbers for r1.** We're trying to keep crap to a minimum; DO NOT add any machine specific* crap here - that's what the boot loader (or in extreme, well justified* circumstances, zImage) is for.*/.section ".text.head", "ax" @ 定义一个属性为“可分配可运行”的名为“.text.head”的节.type	stext, %function @ 声明stext标号为一个函数
ENTRY(stext)	@ 定义全局标号stextmsr	cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode@ and irqs disabled 其实,这里的工作bootloader都已经做了,这里再做一遍保证万无一失。mrc	p15, 0, r9, c0, c0		@ get processor id arm9只有一个协处理器,处理器id存储在c0寄存器,将c0中的cpuid存储到r9中。bl	__lookup_processor_type		@ r5=procinfo r9=cpuid,调用函数__lookup_processor_type,该函数中会根据r9的内容匹配内核代码支持的procinfo,并把procinfo存储在r5中。movs	r10, r5				@ invalid processor (r5=0)?将r5的内容移动到r10,并且影响CPSR的Z位,如果r5=0,则Z=1,否则Z=0。beq	__error_p			@ yes, error 'p',如果Z=1,则跳转到__error_p停止启动。bl	__lookup_machine_type		@ r5=machinfo,调用__lookup_machine_type函数,该函数中会根据r1的内容匹配内核代码支持的机器ID,并把machinfo存储在r5中。movs	r8, r5				@ invalid machine (r5=0)?,该行代码和下一行配合,用来判断r5是否为0,如果为0则跳入__error_a打印错误信息并停止执行。beq	__error_a			@ yes, error 'a'bl	__create_page_tables @ 调用创建临时一级页表函数/* 该段代码比较难以理解,下一小节展开说明* The following calls CPU specific code in a position independent* manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of* xxx_proc_info structure selected by __lookup_machine_type* above.  On return, the CPU will be ready for the MMU to be* turned on, and r0 will hold the CPU control register value.*/ldr	r13, __switch_data		@ address to jump to after,将__switch_data所在地址赋值给r13@ mmu has been enabledadr	lr, __enable_mmu		@ return (PIC) address,将__enable_mmu地址赋值给lr。add	pc, r10, #PROCINFO_INITFUNC 

ENTRY的展开如下:

/* /include/linux/linkage.h 文件 */#define __ALIGN		.align 4,0x90 /*4:4字节对其;0x90:NOP指令的机器码,用于填充到指定的对齐字节*/
#define ALIGN __ALIGN#ifndef ENTRY
#define ENTRY(name) \.globl name; \ /* 声明该标号为全局标号 */ALIGN; \ /* 4字节对齐,使用nop进行填充 */name: /* 定义标号 */
#endif

2.2 __cpu_flush函数

下面的代码使用的技巧性比较强,单独拿出来分析一下。

/** The following calls CPU specific code in a position independent* manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of* xxx_proc_info structure selected by __lookup_machine_type* above.  On return, the CPU will be ready for the MMU to be* turned on, and r0 will hold the CPU control register value.*/
097:  ldr	r13, __switch_data		@ address to jump to after,将__switch_data所在地址赋值给r13
098:  @ mmu has been enabled
099:  adr	lr, __enable_mmu		@ return (PIC) address,将__enable_mmu地址赋值给lr。
100:  add	pc, r10, #PROCINFO_INITFUNC 

从第100行代码开始分析,主要意思是改变pc指针,这就意味着程序的流程会发生改变,那下一个问题就是变到哪里?

pc = r10 + PROCINFO_INITFUNC

前面的初始化保证,寄存器r10指向procinfo,在文件arch/arm/kernel/asm-offsets.c文件中宏PROCINFO_INITFUNC的定义如下:

 DEFINE(PROCINFO_INITFUNC,     offsetof(struct proc_info_list, __cpu_flush));

我们再来看一下结构体proc_info_list:

/** Note!  struct processor is always defined if we're* using MULTI_CPU, otherwise this entry is unused,* but still exists.** NOTE! The following structure is defined by assembly* language, NOT C code.  For more information, check:*  arch/arm/mm/proc-*.S and arch/arm/kernel/head.S*/
struct proc_info_list {unsigned int		cpu_val;unsigned int		cpu_mask;unsigned long		__cpu_mm_mmu_flags;	/* used by head.S */unsigned long		__cpu_io_mmu_flags;	/* used by head.S */unsigned long		__cpu_flush;		/* used by head.S *//*我们关注的字段在这里*/const char		*arch_name;const char		*elf_name;unsigned int		elf_hwcap;const char		*cpu_name;struct processor	*proc;struct cpu_tlb_fns	*tlb;struct cpu_user_fns	*user;struct cpu_cache_fns	*cache;
};

通过上面结构体的注释,我们知道,该结构体的真实定义位置是arch/arm/mm/proc-*.S。此处,我们以proc-arm920.S文件为例分析。

/* 真实的结构体定义如下 */__arm920_proc_info:.long	0x41009200.long	0xff00fff0.long   PMD_TYPE_SECT | \PMD_SECT_BUFFERABLE | \PMD_SECT_CACHEABLE | \PMD_BIT4 | \PMD_SECT_AP_WRITE | \PMD_SECT_AP_READ.long   PMD_TYPE_SECT | \PMD_BIT4 | \PMD_SECT_AP_WRITE | \PMD_SECT_AP_READb	__arm920_setup			/*和C语言中对照偏移,这个就是我们关注的字段*/.long	cpu_arch_name.long	cpu_elf_name.long	HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB.long	cpu_arm920_name.long	arm920_processor_functions.long	v4wbi_tlb_fns.long	v4wb_user_fns
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH.long	arm920_cache_fns
#else.long	v4wt_cache_fns
#endif.size	__arm920_proc_info, . - __arm920_proc_info

我们发现,此数据结构中,C语言结构体中定义的__ cpu_flush成员,在汇编语言中被定义为一条调用指令:b __arm920_setup。

因此 add pc, r10, #PROCINFO_INITFUNC 这句指令辗转(难点就在于名字不对应,但偏移对应)调用了 __arm920_setup 函数。到此,我们不再继续跟踪。


第100行代码调用结束之后,pc = lr,也就是执行 __enable_mmu 函数。

该函数调用了 __turn_mmu_on 函数,关键点在于该函数的最后一行。

pc = r13 会返回第97行执行 __switch_data 函数。

	.align	5.type	__turn_mmu_on, %function
__turn_mmu_on:mov	r0, r0mcr	p15, 0, r0, c1, c0, 0		@ write control regmrc	p15, 0, r3, c0, c0, 0		@ read id regmov	r3, r3mov	r3, r3mov	pc, r13  /* 这里是关键,会返回第97行执行__switch_data函数*/

Tips:总结一下97行到100行代码的难度到底在那里。首先,在于C语言定义的结构体和汇编定义的同一个结构体只有偏移相同,但是名称不同;再次,在于汇编语言对寄存器的分时复用思想,当某个或某些寄存器的时间跨度过大后,对代码的可读性会产生重大破坏,比如上述的r13,中间横跨的代码太多了,不建议这么使用;最后,难度在于大量使用了函数指针,而且还是在汇编中。

不过,这种代码见得多了,有了经验,下次遇到也就不觉得繁琐了。


<完>

http://www.jmfq.cn/news/5124205.html

相关文章:

  • 可以做推文的网站/推广普通话黑板报
  • 互联网与智慧酒店建设/seo程序专员
  • typora wordpress/东莞百度快照优化排名
  • 网页设计师中级证书有用吗/梁水才seo优化专家
  • 简洁的门户网站/seo关键词排名优化教程
  • 自动打开多个同网站网页/sem竞价托管费用
  • 网站欢迎屏怎么做/发外链的论坛
  • 淘宝seo搜索优化/西安企业seo外包服务公司
  • 瑞安app开发/seo外包公司哪家好
  • vs2013做网站/微信朋友圈广告推广代理
  • 上海找做网站公司好/互联网推广渠道有哪些
  • 上海市建交人才网/seo排名优化哪家好
  • 最好的做网站公司有哪些/电商推广和网络推广的区别
  • 中国工程建设标准化协会官方网站/产品如何做网络推广
  • 北京公司网站建设/百度 营销中心
  • 建站案例/武汉电脑培训学校有哪些
  • 自动生成网站地图怎么做/中山seo排名
  • 做铝锭的网站/搜索引擎有哪些平台
  • 哪个网站是专门做装修的/seo企业建站系统
  • 网页设计个人总结/唐山seo排名优化
  • 口碑好的网站建设公司哪家好/搜索引擎有哪些网站
  • 宿迁网站建设价格低/盐城seo网站优化软件
  • 建设图片展示网站/武汉网络关键词排名
  • 税务局网站怎么做财务报表/浙江百度推广
  • 淘宝客做自己的网站/南宁整合推广公司
  • 嘉兴 做企业网站/优化大师手机版
  • 科技建站网站源码/百度提交入口的网址
  • 网站做百度排名/合肥建站公司seo
  • 烟台 网站建设多少钱/百度广告收费
  • 网页版qq安全中心登录入口/seo优化百度技术排名教程