ARMlinux内核启动分析.

ARMlinux内核启动分析.
ARMlinux内核启动分析.

ARM linux内核启动分析[原创 2007-06-11 10:35:46 ] 发表者: jimmy_lee

head-armv.S主支分析

head-armv.S是解压后(或未压缩的内核最先执行的一个文件,这个文件位于

arch/arm/kernel/head-armv.S,在与这个文件同目录下还有一个文件head-armo.S与head-armv.S 很相似,但从arch/arm/下的Makefile中可以看到区别在哪里:

ifeq ($(CONFIG_CPU_26,y

PROCESSOR := armo

ifeq ($(CONFIG_ROM_KERNEL,y

DATAADDR = 0x02080000

TEXTADDR = 0x03800000

LDSCRIPT = arch/arm/vmlinux-armo-rom.lds.in

else

TEXTADDR = 0x02080000

LDSCRIPT = arch/arm/vmlinux-armo.lds.in

endif

endif

ifeq ($(CONFIG_CPU_32,y

PROCESSOR = armv

TEXTADDR = 0xC0008000

LDSCRIPT = arch/arm/vmlinux-armv.lds.in

endif

……

HEAD :=arch/arm/kernel/head-$(PROCESSOR.o \

arch/arm/kernel/init_task.o

闲话少说,在进入分析head-armv.S之前,交待一下我所分析的内核版本号以及硬件平台,内核是2.4.19-rmk7-pxa2,对应的硬件平台为pxa 270。

开篇说到,head-armv.S是进入内核最先执行的文件,为什么呢?内核可执行文件由许多链接在一起的对象文件组成。对象文件有许多节,如文本、数据、init 数据、bass 等等。这些对象文件都是由一个称为link script的文件链接并装入的。这个link script的功能是将输入对象文件的各节映射到输出文件中;换句话说,它将所有输入对象文件都链接到单一的可执行文件中,将该可执行文件的各节装入到指定地址处。vmlinux-armv.lds就是链接内核用到的link script,它位于arch/arm/目录下,你可能注意到了同目录下还有一vmlinux-armv.lds.in文件,这两文件可是有关系的,答案就在arch/arm/Makefile里。

ifeq ($(CONFIG_CPU_32,y /* 对于pxa 270来说这里是True */

PROCESSOR = armv

TEXTADDR = 0xC0008000

LDSCRIPT = arch/arm/vmlinux-armv.lds.in

endif

arch/arm/vmlinux.lds: arch/arm/Makefile $(LDSCRIPT \

$(wildcard include/config/cpu/32.h \

$(wildcard include/config/cpu/26.h \

$(wildcard include/config/arch/*.h

@echo ' Generating $@'

@sed 's/TEXTADDR/$(TEXTADDR/;s/DATAADDR/$(DATAADDR/' $(LDSCRIPT >$@

从这个Makefile中我们可以看到,实际上arch/arm/vmlinux-armv.lds.in就是

arch/arm/vmlinux-armv.lds是一个蓝本,在make的时候vmlinux-armv.lds是由sed命令来替换vmlinux-armv.lds.in文件中的TEXTADDR, DATAADDR为特定的值而生成的。

接下来就来真正看一下vmlinux-armv.lds里面的内容:

OUTPUT_ARCH(arm

ENTRY(stext

SECTIONS

{

. = 0xC0008000;

.init : { /* Init code and data*/

_stext = .;

__init_begin = .;

*(.text.init

__proc_info_begin = .;

*(https://www.360docs.net/doc/a69649293.html,

__proc_info_end = .;

__arch_info_begin = .;

*(https://www.360docs.net/doc/a69649293.html,

__arch_info_end = .;

……

}

……

}

ENTRY(stext,就是说明了最先执行的第一条指令是从stext开始,而这个stext就是位于head-armv.S当中,它被定义于放置于.text.init section,而且.text.init section在vmlinux.lds文件中也是被放置于输出文件的起始位置。

/* arch/arm/kernel/head-armv.S */

93 .section ".text.init",#alloc,#execinstr

94 .type stext, #function

95 ENTRY(stext //内核入口点

96 /*

97 mov r0, #0

98 mov r1, #300

99 add r1, r1, #4

100 */

101 mov r12, r0 //保护r0, r0=0, r12=0

…. /* 这中间的都是与XScale平台无关的code */

186 mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode 187 msr cpsr_c, r0 @ and all irqs disabled

188 bl __lookup_processor_type

189 teq r10, #0 @ invalid processor?

190 moveq r0, #'p' @ yes, error 'p'

191 beq __error

192 bl __lookup_architecture_type

193 teq r7, #0 @ invalid architecture?

194 moveq r0, #'a' @ yes, error 'a'

195 beq __error

196 bl __create_page_tables

197 adr lr, __ret @ return address

198 add pc, r10, #12 @ initialise processor

199 @ (return control reg

在程序注释中有一段对于入口点的说明,说这个入口点一般是在内核自解压缩代码中被调用(关于内核的自解压缩,我将在以后的文章中进行分析),在进入这个入口点前,须满足以下条件:MMU=off,D-Cache=off,I-Cache=don’t care,r0 =0,r1=machine number (see

arch/arm/tools/mach-types.h。

Line93, 定义一个section,名为.text.init,#alloc表示section is allocatable, #execinstr表示section is executable。从前面vmlinux-armv.lds文件中我们可以看到,这个"text.init" section 会放到0xC0008000(在arch/arm/Makefile中TEXTADDR指定这个起始位置。

Line95, 这里的ENTRY其实是一个宏,这个宏位于linux/linkage.h中,在head-armv.S中就包含了这个头文件。

/* linux/linkage.h */

#define SYMBOL_NAME(X X

#ifdef __STDC__

#define SYMBOL_NAME_LABEL(X X##:

#else

#define SYMBOL_NAME_LABEL(X X/**/:

#endif

#ifdef __arm__

#define __ALIGN .align 0

#define __ALIGN_STR ".align 0"

#else

#endif

#define ALIGN __ALIGN

#define ALIGN_STR __ALIGN_STR

#define ENTRY(name \

.globl SYMBOL_NAME(name; \

ALIGN; \

SYMBOL_NAME_LABEL(name

line186~187: r0=0b11010011,用于设置当前程序状态寄存器,以禁止FIQ, IRQ,进入supervisor模式。

Line188:跳转到__lookup_processor_type,读取运行的cpu的ID值,判断此ID值是否被内核所支持,如果不支持,返回时r10为0。

Line189~191: 如果是无效的processor,则跳转到__error。

Line192: 跳转到__lookup_architecture_type,看r1寄存器的architecture number值是否支持,如果不支持,则返回时r7=0。Bootloader引导linux kernel时,会传递给r1为machine number。Line193~195: 如果是无效的(不支持的)体系类型(r7=0,则跳转到__error。

Line196:创建核心页表。

Line197: 将标号__ret的地址放入 lr 寄存器。__ret标号处相关源码如下:

/* arch/arm/kernel/head-armv.S */

216 .type __ret, %function

217 __ret: ldr lr, __switch_data

218 mcr p15, 0, r0, c1, c0

219 mrc p15, 0, r0, c1, c0, 0 @ read it back.

220 mov r0, r0

221 mov r0, r0

222 mov pc, lr

也就是说,在将来某个时候有可能会调用mov pc, lr语句(实际上会在arch/arm/mm/proc-xscale.S文件中__xscale_setup函数的line942调用,这在后面会讲到)会跳转到line217执行ldr lr, __switch_data。

Line198:r10+12->pc,也即程序跳转到r10+12的地址处执行,此时的r10存储的是在执行

__lookup_processor_type函数后得到的当前处理器信息、是一个proc_info_list的结构体信息,对于我们的pxa270平台,r10指向的就是arch/arm/mm/proc-xscale.S文件中line1099处那个位置,也就是说从line1099~1112的内容就是一个proc_info_list的结构体实例。

/* arch/arm/mm/proc-xscale.S */

1097 .type __bva0_proc_info,#object

1098 __bva0_proc_info:

1099 .long 0x69054110 @ Bulverde A0: 0x69054110, A1 : 0x69054111.

1100 .long 0xfffffff0 @ and this is the CPU id mask.

1101 #if CACHE_WRITE_THROUGH

1102 .long 0x00000c0a

1103 #else

1104 .long 0x00000c0e

1105 #endif

1106 b __xscale_setup

1107 .long cpu_arch_name

1108 .long cpu_elf_name

1109 .long

HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_ XSCALE

1110 .long cpu_bva0_info

1111 .long xscale_processor_functions

1112 .size __bva0_proc_info, . - __bva0_proc_info

而r10+12就是line1106(b __xscale_setup)。__xscale_setup函数是实际的CPU的设置子程序,它主要是操作协处理器,设置页表目录项基地址,对CACHE和BUFFER的控制位进行一些操作(关于__xscale_setup的源码分析放在后面的分支分析中)。也就是说line198执行的

结果是跳转到arch/arm/mm/proc-xscale.S的line1106。(关于该行源码建议读者先分析完

__lookup_processor_type函数再看。__xscale_setup函数返回程序会跳转到line217。

Line217:将__switch_data位置处的值放入lr(注意这里是指令ldr,不是把标号__switch_data 的地址放入lr),将来在line222时会跳转到__swith_data所指向的地址。__swith_data标号相关源码如下:

/* arch/arm/kernel/head-armv.S */

201 .type __switch_data, %object

202 __switch_data: .long __mmap_switched

203 .long SYMBOL_NAME(__bss_start

204 .long SYMBOL_NAME(_end

205 .long SYMBOL_NAME(processor_id

206 .long SYMBOL_NAME(__machine_arch_type

207 .long SYMBOL_NAME(cr_alignment

208 .long SYMBOL_NAME(init_task_union+8192

Line222:因为line217将lr的值存储为__swith_data标号处的值,即__mmap_switched标号的地址,则此行执行的结果是跳转到__mmap_switched函数(line233处。而__mmap_switched函数相关源码如下:

/* arch/arm/kernel/head-armv.S */

224 /*

225 * The following fragment of code is executed with the MMU on, and uses 226 * absolute addresses; this is not position independent.

227 *

228 * r0 = processor control register

229 * r1 = machine ID

230 * r9 = processor ID

231 */

232 .align 5

233 __mmap_switched:

234 #ifdef CONFIG_XIP_KERNEL // 对于pxa270 此处为false,故code略……

243 #endif

244

245 adr r3, __switch_data + 4

246 ldmia r3, {r4, r5, r6, r7, r8, sp}@ r2 = compat

247 @ sp = stack pointer

248

249 mov fp, #0 @ Clear BSS (and zero fp

250 1: cmp r4, r5

251 strcc fp, [r4],#4

252 bcc 1b

253

254 str r9, [r6] @ Save processor ID

255 str r1, [r7] @ Save machine type

256 #ifdef CONFIG_ALIGNMENT_TRAP

257 orr r0, r0, #2 @ ...........A.

258 #endif

259 bic r2, r0, #2 @ Clear 'A' bit

260 stmia r8, {r0, r2} @ Save control register values

261 b SYMBOL_NAME(start_kernel

Line233~261:这段代码的作用主要是在进入C函数前先做一些变量的初始化和保存工作。首先清空BSS区域,然后保存处理器ID和机器类型到各自变量地址,接着保存cr_alignment,最后跳转到init/main.c中的start_kernel函数运行。

再来具体分析一下这里面的代码,line245~246,它的结果就是r4 =__bss_start, r5=_end, r6=processor_id, r7=__machine_arch_type,r8=cr_alignment,sp= init_task_union+8192,这些寄存器存储的都是变量的地址。

再看一下line254,r9之前存储的是在__lookup_processor_type获取的processor id,这里把process id放置在r6指向的内存,r6此时由于在line246被赋予了processor_id变量的指针,所以这里把process id 保存在变量processor_id中(processor_id在arch/arm/kernel/setup.c中被定义。

同样,line255,r1之前存储的是在kernel booting前由bootloader传递过来的machine number,在这里把machine number放置到r7所指向的内存,r7在line246被赋予了指向

__machine_arch_type变量,所以这里把machine number保存在变量__machine_arch_type中(__machine_arch_type同样也定义在arch/arm/kernel/setup.c中)。

ARM linux内核启动分析(2 [原创 2007-06-11 10:50:25 ] 发表者: jimmy_lee

--------------------------------------

分支1:__lookup_processor_type函数分析

内核中使用了一个结构struct proc_info_list,用来记录处理器相关的信息,该结构定义在kernel/include/asm-arm/procinfo.h头文件中。

/* arch/arm/kernel/head-armv.S */

/*

* 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-armv.S

*/

struct proc_info_list {

unsigned int cpu_val;

在arch/arm/mm/proc-xscale.S文件中定义了所有和xscale有关的

proc_info_list,我们使用的pxa270定义如下:

/* arch/arm/mm/proc-xscale.S */

1027 .section "https://www.360docs.net/doc/a69649293.html,", #alloc, #execinstr

1028

1029 .type __80200_proc_info,#object

1030 __80200_proc_info: //每一个__xxx_proc_info就对应于proc_info_list 1031 .long 0x69052000 //cpu_val

1032 .long 0xfffffff0 //cpu_mask

1033 #if CACHE_WRITE_THROUGH

1034 .long 0x00000c0a //__cpu_mmu_flags

1035 #else

1036 .long 0x00000c0e //__cpu_mmu_flags

1037 #endif

1038 b __xscale_setup //__cpu_flush

1039 .long cpu_arch_name //arch_name

1040 .long cpu_elf_name //elf_name

1041 .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT| HWCAP_EDSP|HWCAP_XSCALE // elf_hwcap

1042 .long cpu_80200_info // info

1043 .long xscale_processor_functions //proc

1044 .size __80200_proc_info, . - __80200_proc_info

1045

….

1096

1097 .type __bva0_proc_info,#object

1098 __bva0_proc_info: //pxa270 的proc_info_list

1099 .long 0x69054110 @ Bulverde A0: 0x69054110, A1 : 0x69054111. 1100 .long 0xfffffff0 @ and this is the CPU id mask.

1101 #if CACHE_WRITE_THROUGH

1102 .long 0x00000c0a

1103 #else

1104 .long 0x00000c0e

1105 #endif

1106 b __xscale_setup

1107 .long cpu_arch_name

在上述code中,由于定义了section,因此__bva0_proc_info等在编译时会放入https://www.360docs.net/doc/a69649293.html, section中。根据vmlinux-armv.lds,__arch_info_begin与

__arch_info_end就分别指向https://www.360docs.net/doc/a69649293.html, section的起始和结束,换句话说,在__arrch_info_begin与__arch_info_end所指向的区域,包含着若干个

proc_info_list结构的数据。这个会在__lookup_processor_type中用到,下面来看一下__lookup_processor_type这个分支代码。

/* arch/arm/kernel/head-armv.S */

477 __lookup_processor_type:

478 adr r5, 2f //把标号2(行498)的址址放入r5寄存器中

479 ldmia r5, {r7, r9, r10} //r7 = __proc_info_end

// r9 = __proc_info_begin

// r10 = 标号2(行498)的地址

480 sub r5, r5, r10 @ convert addresses

481 add r7, r7, r5 @ to our address space

482 add r10, r9, r5

483 mrc p15, 0, r9, c0, c0 @ get processor id

484 1: ldmia r10, {r5, r6, r8} @ value, mask, mmuflags

485 and r6, r6, r9 @ mask wanted bits

486 teq r5, r6

487 moveq pc, lr

488 add r10, r10, #36 @ sizeof(proc_info_list

489 cmp r10, r7

490 blt 1b

491 mov r10, #0 @ unknown processor

492 mov pc, lr

493

494 /*

495 * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for

496 * more information about the __proc_info and __arch_info structures.

497 */

498 2: .long __proc_info_end

499 .long __proc_info_begin

500 .long 2b

501 .long __arch_info_begin

502 .long __arch_info_end

Line478:将line498的地址放到寄存器r5中,adr是小范围的地址读取伪指令。

Line479:将r5所指向的数据区的数据读出到r7,r9,r10。结果就是r7=

__proc_info_end,r9 = __proc_info_begin, r10 = 标号2(line498)的地址。

Line480~482:r10 = __proc_info_begin。

Line483:mrc是一个协处理器寄存器到ARM寄存器的数据传送指令,它的指令格式是:MRC coproc, opcode1, Rd, CRn {, opcode2} 对于读取Processor ID来说,coproc 为15,opcode1为0, CRn=0, opcode2=0。Line483读取processor ID,并放入r9寄存器中。

Line484~490,是一个循环处理过程,由于r10一开始是指向__proc_info_begin 的,前面提到__proc_info_begin指向https://www.360docs.net/doc/a69649293.html, section的首地址,所以整个循环就是遍历https://www.360docs.net/doc/a69649293.html, section,也就是遍历proc_info_list结构数组,比较(proc_info_list[i]->cpu_val == processor ID & proc_info_list[i]-

>cpu_mask是否与当前运行的processor ID一致,如果有条件满足的则函数在line487返回(跳转到line189,此时r10是不为零的(因为函数返回后,要根据r10是否为0,来判断processor未知与否);如果遍历完整个https://www.360docs.net/doc/a69649293.html, section都不满足的,则进入line491。

Line491~492:置r10=0用来表示processor 未知,然后函数返回(跳转到line189。

作者:谷丰,您可以通过[email]g u f e n g 7 7 @ 1 2 6 . c o m[/email]和他联系,转载请包含以上内容

Linux启动后执行的第一个文件是arch/arm/kernel下的head-($PROCESSOR.S文件,processor代表的是该cpu的类型。ARM 6及其以后的处理器核心支持32位地址空间。这些处理器可以在26位和 32位PC的模式下操作。在26位PC模式下,R15 寄存器的表现如同在以前的处理器上,代码只能运行在地址空间的最低的

64M字节空间中。在32位PC模式下,32位的R15寄存器被用做程序计数器。使用独立的状态寄存器来存储处理器模式和状态标志。对于26位的arm处理器类型,linux用armo来表示;对于32位的arm处理器,使用armv表示。在

include/linux/autoconf.h文件中通过

#define CONFIG_CPU_32 1

将处理器类型设置为支持32位PC模式。然后在arch/arm/Makefile中通过

ifeq ($(CONFIG_CPU_32,y

PROCESSOR = armv

TEXTADDR = 0xC0008000

LDSCRIPT = arch/arm/vmlinux-armv.lds.in

endif

设置处理器类型为armv,这样linux运行所执行的第一个文件就是head-armv.S。接着,Makefile定义了内核中代码和数据所使用的虚拟地址TEXRADDR,最后,定义了链接器所使用的脚本文件,这个文件也是与处理器类型相关的。

在执行head-armv.S文件之前,有一点需要注意的是,bootloader已经在处理器的R1寄存器中存放了机器体系结构的类型号。由于在文件的执行过程中将要针对当前的机器体系结构设置相关的参数,如果没有这个步骤,系统将显示“ERROR:a”,同时停止执行。当然,也可以在head -armv.S文件的开头添加代码,手工对R1赋值,具体的机器类型号在arch/arm/tools/mach-types文件中。

好了,接下来我们可以开始阅读head-armv.S文件了,看看它到底作了些什么事情。由于篇幅的限制,对一些不是很关键的代码和英文注释予以省略,但是在每段代码后,我会根据自己的理解给出解释。

#if (TEXTADDR & 0xffff != 0x8000

#error TEXTADDR must start at 0xXXXX8000

#endif

.globl SYMBOL_NAME(swapper_pg_dir

.equ SYMBOL_NAME(swapper_pg_dir, TEXTADDR - 0x4000

.macro pgtbl, reg, rambase

adr \reg, stext

sub \reg, \reg, #0x4000

.endm

.macro krnladr, rd, pgtable, rambase

bic \rd, \pgtable, #0x000ff000

.endm

首先,系统确保TEXTADDR的地址是以0x8000结尾的,前面已经提到过,TEXTADDR的地址是0xC0008000,是内核所使用的虚拟地址,而我所使用的PXA255处理器上支持的SDRAM空间是从0xA0000000开始的,这就需要通过MMU进行虚拟地址到实际物理地址的转换,也就是说将0xC0008000映射到

0xA0008000。地址转换所使用的页表将存放在从0xA0008000网上的16K空间中,即从 0xA0004000到0xA0008000这一段。因此,系统必须空出0xA0000000

到0xA0008000这一段,存放页表和其它的一些内核将使用到的数据结构。虽然上面的代码判断的是TEXTADDR的地址是否以0x8000结尾,但从效果上说是一样

详解bootloader的执行流程与ARM Linux启动过程分析

详解bootloader的执行流程与ARM Linux启动过程分析 ARM Linux启动过程分析是本文要介绍的内容,嵌入式Linux 的可移植性使得我们可以在各种电子产品上看到它的身影。对于不同体系结构的处理器来说Linux的启动过程也有所不同。 本文以S3C2410 ARM处理器为例,详细分析了系统上电后bootloader的执行流程及ARM Linux的启动过程。 1、引言 Linux 最初是由瑞典赫尔辛基大学的学生Linus Torvalds在1991 年开发出来的,之后在GNU的支持下,Linux 获得了巨大的发展。虽然Linux 在桌面PC 机上的普及程度远不及微软的Windows 操作系统,但它的发展速度之快、用户数量的日益增多,也是微软所不能轻视的。而近些年来Linux 在嵌入式领域的迅猛发展,更是给Linux 注入了新的活力。 一个嵌入式Linux 系统从软件角度看可以分为四个部分:引导加载程序(bootloader),Linux 内核,文件系统,应用程序。 其中bootloader是系统启动或复位以后执行的第一段代码,它主要用来初始化处理器及外设,然后调用Linux 内核。 Linux 内核在完成系统的初始化之后需要挂载某个文件系统做为根文件系统(Root Filesystem)。 根文件系统是Linux 系统的核心组成部分,它可以做为Linux 系统中文件和数据的存储区域,通常它还包括系统配置文件和运行应用软件所需要的库。 应用程序可以说是嵌入式系统的“灵魂”,它所实现的功能通常就是设计该嵌入式系统所要达到的目标。如果没有应用程序的支持,任何硬件上设计精良的嵌入式系统都没有实用意义。 从以上分析我们可以看出bootloader 和Linux 内核在嵌入式系统中的关系和作用。Bootloader在运行过程中虽然具有初始化系统和执行用户输入的命令等作用,但它最根本

linux启动过程

Linux系统启动过程分析 by 王斌斌 binbinwang118@https://www.360docs.net/doc/a69649293.html, Linux系统启动过程分析 操作系统的启动过程,实际上是控制权移交的过程。Linux 系统启动包含四个主要的阶段:BIOS initialization, boot loader, kernel initialization, and init startup.见下图: 阶段一、BIOS initialization,主要功能如下: 1.Peripherals detected 2.Boot device selected 3.First sector of boot device read and executed 系统上电开机后,主板BIOS(Basic Input / Output System)运行POST(Power on self test)代码,检测系统外围关键设备(如:CPU、内存、显卡、I/O、键盘鼠标等)。硬件配置信息及一些用户配置参数存储在主板的CMOS( Complementary Metal Oxide Semiconductor)上(一般64字节),实际上就是主板上一块可读写的RAM芯片,由主板上的电池供电,系统掉电后,信息不会丢失。 执行POST代码对系统外围关键设备检测通过后,系统启动自举程序,根据我们在BIOS中设置的启动顺序搜索启动驱动器(比如的硬盘、光驱、网络服务器等)。选择合适的启动器,比如通常情况下的硬盘设备,BIOS会读取硬盘设备的第一个扇区(MBR,512字节),并执行其中的代码。实际上这里BIOS并不关心启动设备第一个扇区中是什么内容,它只是负责读取该扇区内容、并执行,BIOS的任务就完成了。此后将系统启动的控制权移交到MBR部分的代码。 注:在我们的现行系统中,大多关键设备都是连在主板上的。因此主板BIOS提供了一个操作系统(软件)和系统外围关键设备(硬件)最底级别的接口,在这个阶段,检测系统外围关键设备是否准备好,以供操作系 “” 统使用。 阶段二、Boot Loader 关于Boot Loader,简单的说就是启动操作系统的程序,如grub,lilo,也可以将boot loader本身看成一个小系统。 The BIOS invokes the boot loader in one of two ways: 1.It pass control to an initial program loader (IPL) installed within a driver's Master Boot Record (MBR) 2.It passes control to another boot loader, which passes control to an IPL installed within a partition's boot sector. In either case, the IPL must exist within a very small space, no larger than 446 bytes. Therefore, the IPL for GRUB is merely a first stage, whose sole task is to locate and load a second stage boot loader, which does most of the work to boot the system. There are two possible ways to configure boot loaders: Primary boot loader: Install the first stage of your Linux boot loader into the MBR. The boot loader must be configure to pass control to any other desired operating systems. Secondary boot loader: Install the first stage of your Linux boot loader into the boot sector of some partition. Another boot loader must be installed into the MBR, and configured to pass control to your Linux boot loader. 假设Boot Loader 为grub (grub-0.97),其引导系统的过程如下: grub 分为stage1 (stage1_5) stage2两个阶段。stage1 可以看成是initial program loaderI(IPL),而stage2则实现了grub 的主要功能,包括对特定文件系统的支持(如ext2,ext3,reiserfs等),grub自己的shell,以及内部程序(如:kernrl,initrd,root )等。

Linux内核崩溃原因分析及错误跟踪技术

Linux内核崩溃原因分析及错误跟踪技术 随着嵌入式Linux系统的广泛应用,对系统的可靠性提出了更高的要求,尤其是涉及到生命财产等重要领域,要求系统达到安全完整性等级3级以上[1],故障率(每小时出现危险故障的可能性)为10-7以下,相当于系统的平均故障间隔时间(MTBF)至少要达到1141年以上,因此提高系统可靠性已成为一项艰巨的任务。对某公司在工业领域14 878个控制器系统的应用调查表明,从2004年初到2007年9月底,随着硬软件的不断改进,根据错误报告统计的故障率已降低到2004年的五分之一以下,但查找错误的时间却增加到原来的3倍以上。 这种解决问题所需时间呈上升的趋势固然有软件问题,但缺乏必要的手段以辅助解决问题才是主要的原因。通过对故障的统计跟踪发现,难以解决的软件错误和从发现到解决耗时较长的软件错误都集中在操作系统的核心部分,这其中又有很大比例集中在驱动程序部分[2]。因此,错误跟踪技术被看成是提高系统安全完整性等级的一个重要措施[1],大多数现代操作系统均为发展提供了操作系统内核“崩溃转储”机制,即在软件系统宕机时,将内存内容保存到磁盘[3],或者通过网络发送到故障服务器[3],或者直接启动内核调试器[4]等,以供事后分析改进。 基于Linux操作系统内核的崩溃转储机制近年来有以下几种: (1) LKCD(Linux Kernel Crash Dump)机制[3]; (2) KDUMP(Linux Kernel Dump)机制[4]; (3) KDB机制[5]; (4) KGDB机制[6]。 综合上述几种机制可以发现,这四种机制之间有以下三个共同点: (1) 适用于为运算资源丰富、存储空间充足的应用场合; (2) 发生系统崩溃后恢复时间无严格要求; (3) 主要针对较通用的硬件平台,如X86平台。 在嵌入式应用场合想要直接使用上列机制中的某一种,却遇到以下三个难点无法解决: (1) 存储空间不足 嵌入式系统一般采用Flash作为存储器,而Flash容量有限,且可能远远小于嵌入式系统中的内存容量。因此将全部内存内容保存到Flash不可行。

linux内核启动 Android系统启动过程详解

linux内核启动+Android系统启动过程详解 第一部分:汇编部分 Linux启动之 linux-rk3288-tchip/kernel/arch/arm/boot/compressed/ head.S分析这段代码是linux boot后执行的第一个程序,完成的主要工作是解压内核,然后跳转到相关执行地址。这部分代码在做驱动开发时不需要改动,但分析其执行流程对是理解android的第一步 开头有一段宏定义这是gnu arm汇编的宏定义。关于GUN 的汇编和其他编译器,在指令语法上有很大差别,具体可查询相关GUN汇编语法了解 另外此段代码必须不能包括重定位部分。因为这时一开始必须要立即运行的。所谓重定位,比如当编译时某个文件用到外部符号是用动态链接库的方式,那么该文件生成的目标文件将包含重定位信息,在加载时需要重定位该符号,否则执行时将因找不到地址而出错 #ifdef DEBUG//开始是调试用,主要是一些打印输出函数,不用关心 #if defined(CONFIG_DEBUG_ICEDCC)

……具体代码略 #endif 宏定义结束之后定义了一个段, .section ".start", #alloc, #execinstr 这个段的段名是 .start,#alloc表示Section contains allocated data, #execinstr表示Section contains executable instructions. 生成最终映像时,这段代码会放在最开头 .align start: .type start,#function /*.type指定start这个符号是函数类型*/ .rept 8 mov r0, r0 //将此命令重复8次,相当于nop,这里是为中断向量保存空间 .endr b 1f .word 0x016f2818 @ Magic numbers to help the loader

linux、内核源码、内核编译与配置、内核模块开发、内核启动流程

linux、内核源码、内核编译与配置、内核模块开发、内核启动流程(转) linux是如何组成的? 答:linux是由用户空间和内核空间组成的 为什么要划分用户空间和内核空间? 答:有关CPU体系结构,各处理器可以有多种模式,而LInux这样的划分是考虑到系统的 安全性,比如X86可以有4种模式RING0~RING3 RING0特权模式给LINUX内核空间RING3给用户空间 linux内核是如何组成的? 答:linux内核由SCI(System Call Interface)系统调用接口、PM(Process Management)进程管理、MM(Memory Management)内存管理、Arch、 VFS(Virtual File Systerm)虚拟文件系统、NS(Network Stack)网络协议栈、DD(Device Drivers)设备驱动 linux 内核源代码 linux内核源代码是如何组成或目录结构? 答:arc目录存放一些与CPU体系结构相关的代码其中第个CPU子目录以分解boot,mm,kerner等子目录 block目录部分块设备驱动代码 crypto目录加密、压缩、CRC校验算法 documentation 内核文档 drivers 设备驱动 fs 存放各种文件系统的实现代码 include 内核所需要的头文件。与平台无关的头文件入在include/linux子目录下,与平台相关的头文件则放在相应的子目录中 init 内核初始化代码 ipc 进程间通信的实现代码 kernel Linux大多数关键的核心功能者是在这个目录实现(程序调度,进程控制,模块化) lib 库文件代码 mm 与平台无关的内存管理,与平台相关的放在相应的arch/CPU目录net 各种网络协议的实现代码,注意而不是驱动 samples 内核编程的范例 scripts 配置内核的脚本 security SElinux的模块 sound 音频设备的驱动程序 usr cpip命令实现程序 virt 内核虚拟机 内核配置与编译 一、清除 make clean 删除编译文件但保留配置文件

探究linux内核,超详细解析子系统

探究linux内核,超详细解析子系统 Perface 前面已经写过一篇《嵌入式linux内核的五个子系统》,概括性比较强,也比较简略,现在对其进行补充说明。 仅留此笔记,待日后查看及补充!Linux内核的子系统 内核是操作系统的核心。Linux内核提供很多基本功能,如虚拟内存、多任务、共享库、需求加载、共享写时拷贝(Copy-On-Write)以及网络功能等。增加各种不同功能导致内核代码不断增加。 Linux内核把不同功能分成不同的子系统的方法,通过一种整体的结构把各种功能集合在一起,提高了工作效率。同时还提供动态加载模块的方式,为动态修改内核功能提供了灵活性。系统调用接口用户程序通过软件中断后,调用系统内核提供的功能,这个在用户空间和内核提供的服务之间的接口称为系统调用。系统调用是Linux内核提供的,用户空间无法直接使用系统调用。在用户进程使用系统调用必须跨越应用程序和内核的界限。Linux内核向用户提供了统一的系统调用接口,但是在不同处理器上系统调用的方法

各不相同。Linux内核提供了大量的系统调用,现在从系统 调用的基本原理出发探究Linux系统调用的方法。这是在一个用户进程中通过GNU C库进行的系统调用示意图,系 统调用通过同一个入口点传入内核。以i386体系结构为例,约定使用EAX寄存器标记系统调用。 当加载了系统C库调用的索引和参数时,就会调用0x80软件中断,它将执行system_call函数,这个函数按照EAX 寄存器内容的标示处理所有的系统调用。经过几个单元测试,会使用EAX寄存器的内容的索引查system_call_table表得到系统调用的入口,然后执行系统调用。从系统调用返回后,最终执行system_exit,并调用resume_userspace函数返回用户空间。 linux内核系统调用的核心是系统多路分解表。最终通过EAX寄存器的系统调用标识和索引值从对应的系统调用表 中查出对应系统调用的入口地址,然后执行系统调用。 linux系统调用并不单层的调用关系,有的系统调用会由

linux grub 引导启动过程详解

linux grub 引导启动过程详解 2008-01-08 17:18 这几天看了很多文档,算是对linux的启动过程有了比较细致的了解. 网上有很多文章谈到这方面的内容,但总觉得没有一篇完全的解析linux启动的 细节,下面是我小弟在学习的过程中总结出来的一些东东.这个是完整的linux启动过程, 不涉及内核,但是我觉得比较详细哦. (由于本人比较懒,这一段是从网上抄的) 机器加电启动后,BIOS开始检测系统参数,如内存的大小,日期和时间,磁盘 设备以及这些磁盘设备用来引导的顺序,通常情况下,BIOS都是被配置成首先检查 软驱或者光驱(或两者都检查),然后再尝试从硬盘引导。如果在这些可移动的设 备中,没有找到可引导的介质,那么BIOS通常是转向第一块硬盘最初的几个扇区, 寻找用于装载操作系统的指令。装载操作系统的这个程序就是boot loader. linux里面的boot loader通常是lilo或者grub,从Red Hat Linux 7.2起,GRUB( GRand Unified Bootloader)取代LILO成为了默认的启动装载程序。那么启动的时候grub是如何被载入的呢 grub有几个重要的文件,stage1,stage2,有的时候需要stage1.5.这些文件一般都 在/boot/grub文件夹下面.grub被载入通常包括以下几个步骤: 1. 装载基本的引导装载程序(stage1),stage1很小,网上说是512字节,但是在我的系统上用du -b /boot/grub/stage1 显示的是1024个字节,不知道是不是grub版本不同的缘故还是我理解有误.stage1通常位于主引导扇区里面,对于硬盘就是MBR了,stage1的主要功能就是装载第二引导程序(stage2).这主要是归结于在主引导扇区中没有足够的空间用于其他东西了,我用的是grub 0.93,stage2文件的大小是107520 bit. 2. 装载第二引导装载程序(stage2),这第二引导装载程序实际上是引出更高级的功能, 以允许用户装载入一个特定的操作系统。在GRUB中,这步是让用户显示一个菜单或是输入命令。由于stage2很大,所以它一般位于文件系统之中(通常是boot所在的根 分区). 上面还提到了stage1.5这个文件,它的作用是什么呢你到/boot/grub目录下看看, fat_stage_1.5 e2fs_stage_1.5 xfs_stage_1.5等等,很容易猜想stage1.5和文件系统 有关系.有时候基本引导装载程序(stage1)不能识别stage2所在的文件系统分区,那么这 时候就需要stage1.5来连接stage1和stage2了.因此对于不同的文件系统就会有不同的stage1.5.但是对于grub 0.93好像stage1.5并不是很重要,因为我试过了,在没有stage1.5 的情况下, 我把stage1安装在软盘的引导扇区内,然后把stage2放在格式化成ext2或者fat格式的软盘内,启动的时候照常引导,并不需要e2fs_stage_1.5或者fat_stage_1.5. 下面是我的试验: #mkfs.ext2 /dev/fd0 #mount -t ext2 /dev/fd0 /mnt/floppy #cd /mnt/floppy #mkdir boot #cd boot #mkdir grub (以上三步可用mkdir -p boot/grub命令完成) #cd grub #cp /boot/grub/{stage1,stage2,grub.conf} ./ #cd; umount /mnt/floppy

linux内核IMQ源码实现分析

本文档的Copyleft归wwwlkk所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性,严禁用于任何商业用途。 E-mail: wwwlkk@https://www.360docs.net/doc/a69649293.html, 来源: https://www.360docs.net/doc/a69649293.html,/?business&aid=6&un=wwwlkk#7 linux2.6.35内核IMQ源码实现分析 (1)数据包截留并重新注入协议栈技术 (1) (2)及时处理数据包技术 (2) (3)IMQ设备数据包重新注入协议栈流程 (4) (4)IMQ截留数据包流程 (4) (5)IMQ在软中断中及时将数据包重新注入协议栈 (7) (6)结束语 (9) 前言:IMQ用于入口流量整形和全局的流量控制,IMQ的配置是很简单的,但很少人分析过IMQ的内核实现,网络上也没有IMQ的源码分析文档,为了搞清楚IMQ的性能,稳定性,以及借鉴IMQ的技术,本文分析了IMQ的内核实现机制。 首先揭示IMQ的核心技术: 1.如何从协议栈中截留数据包,并能把数据包重新注入协议栈。 2.如何做到及时的将数据包重新注入协议栈。 实际上linux的标准内核已经解决了以上2个技术难点,第1个技术可以在NF_QUEUE机制中看到,第二个技术可以在发包软中断中看到。下面先介绍这2个技术。 (1)数据包截留并重新注入协议栈技术

(2)及时处理数据包技术 QoS有个技术难点:将数据包入队,然后发送队列中合适的数据包,那么如何做到队列中的数

激活状态的队列是否能保证队列中的数据包被及时的发送吗?接下来看一下,激活状态的队列的 证了数据包会被及时的发送。 这是linux内核发送软中断的机制,IMQ就是利用了这个机制,不同点在于:正常的发送队列是将数据包发送给网卡驱动,而IMQ队列是将数据包发送给okfn函数。

简析linux内核的内核执行流程图

简析linux核的执行流程 ----从bootsect.s到main.c(核版本0.11)Linux启动的第一阶段(从开机到main.c) 3个任务: A、启动BIOS,准备实模式下的中断向量表和中断服务程序。 B、从启动盘加载操作系统程序到存。 C、为执行32的main函数做过渡准备。 存变化如下: ①、0xFE000到0xFFFFF是BIOS启动块,其中上电后第一条指令在0xFFFF0。 ②、而后0x00000到0x003FF总共1KB存放中断向量表,而接下去的地址到0x004FF共256B存放BIOS数据,从0x0E05B 开始的约8KB的存中存放中断服务程序。 ③、利用BIOS中断0x19h把硬盘的第一扇区bootsect.s的代码加载到存中,即0x07c00处,后转到该处执行。 ④、将bootsect.s的代码复制到0x90000处。 ⑤、利用中断0x13h将setup.s程序加载到存0x90200处。 ⑥、再将剩余的约240个扇区的容加载到0x10000~0x2EFFF 处。 ⑦、开始转到setup.s处执行,第一件事就利用BIOS提供的中断服务程序从设备上获取核运行的所需系统数据并存在0x90000的地址处,这时将原来bootsect.s的代码覆盖得只剩2Byte的空间。

⑧、关中断并将系统代码复制到0x00000处,将原来放在这里的中断向量表与BIOS数据区覆盖掉,地址围是 0x00000~0x1EFFF。同时制作两表与两寄存器。 ⑨开地址线A20,寻址空间达到4GB,后对8259重新编程,改变中断号。 ⑩、转到head.s(大小是25K+184B)执行,执行该程序完后是这样的: 0x00000~0x04FFF:页目录与4个页表,每一项是4KB,共20KB;0x05000~0x05400:共1KB的空间是软盘缓冲区; 0x05401~0x054b8:共184B没用; 0x054b9~0x05cb8:共2KB的空间存中断描述符表; 0x05cb9~0x064b8:共2KB的空间存全局描述符表; 之后就是main函数的代码了! 第二阶段、从main.c函数到系统准备完毕阶段。 第一步:创建进程0,并让进程0具备在32位保护模式下载主机中的运算能力。流程是: 复制根设备和硬盘参数表(main.c中的102、110、111行) 物理存规划格局(main.c的112行~126行,其中有 rd_init函数定义在kernel/ramdisk.c中,此函数用于虚拟盘初始化;而mem_init函数是用于存管理结构初始化,定义在mem/memory.c中,该函数页面使用

Linux启动过程详解

深入浅出:Linux的启动流程刨析 Linux的启动过程,是一个Linuxer必须要熟练掌握的。通过系统的启动过程,可以更深入的理解Linux,假如Linux系统出问题的话,可以通过启动过程来分析原因,解决问题。而且,在掌握了Linux的启动流程后,还可以借助宿主机来打造自己的Linux。 下面是我画的一张简单的Linux启动流程图 在了解启动流程之前,我们应该先知道系统的几个重要脚本和配置文件,他们对应的路径为: 1、/sbin/init 2、/etc/inittab 3、/etc/rc.d/rc.sysinit 4、/etc/rc.d/rcN.d //这是几个文件夹N代表数字1,2,3,4.. 5、/etc/fstab 1、关于/sbin/init与/etc/inittab 关于/sbin/init ,它是一个二进制可执行文件,为系统的初始化程序,而/etc/inittab是它的配置文件,我们可以通过/etc/inittab来一睹它的功能,里面的内容是一种固定的文本格式,id:runlevels:action:process 我们来通过它的内容来学习它之前,先了解写运行级别的分类(0-6): 0:关机half

1:单用户模式singel user 2:多用户模式multi user ,不提供nfs服务without nfs 3:完全多用户字符模式full multiuser text mod 4:系统预留officially undefined 5:图形登录界面graphical login 6:重启reboot id:3:initdefault: //这里定义linux的启动时的运行级别,可以看到我的主机的启动级别是3 # System initialization. si::sysinit:/etc/rc.d/rc.sysinit //紧接着,运行系统第一个脚本/etc/rc.d/rc/sysinit //它的action:sysyinit指的是定义系统初始化过程 l0:0:wait:/etc/rc.d/rc 0 l1:1:wait:/etc/rc.d/rc 1 l2:2:wait:/etc/rc.d/rc 2 //然后就是加载服务了,他们被定义在/etc/rc.d/rcN.d l3:3:wait:/etc/rc.d/rc 3 //action:waite 这个进程在在对应级别启动一次,知道它结束为止,我的系统启动级别为3,所有执行rc 3对应的服务 l4:4:wait:/etc/rc.d/rc 4 l5:5:wait:/etc/rc.d/rc 5 l6:6:wait:/etc/rc.d/rc 6 ca::ctrlaltdel:/sbin/shutdown -t3 -r now //这里定义了一个组合快捷键,熟悉吧,没错就是重启,你可以把它注释掉不用 pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"//这里定义了ups电源,powerfail 指的是如果突然断电,它对应的process命令是,提示用户系统电源失效,将要关机,提醒用户把数据都存储好 pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"//这里的action,powerokwaite,指的是系统恢复供电,关机取消...

 1:2345:respawn:/sbin/mingetty tty1 //开启终端,在系统准备工作做好后,就会启动出6个终端,tty1~6 mingetyy就是终端的执行命令 2:2345:respawn:/sbin/mingetty tty2 //可以看到他们对应的级别是2345,你也可以注释

linux内核启动时几个关键地址

linux内核启动时几个关键地址 1、名词解释 ZTEXTADDR 解压代码运行的开始地址。没有物理地址和虚拟地址之分,因为此时MMU处于关闭状态。这个地址不一定时RAM的地址,可以是支持读写寻址的flash等存储中介。 ZRELADDR 内核启动在RAM中的物理地址。压缩的内核映像被解压到这个地址,然后执行。 This is the address where the decompressed kernel will be written, and eventually executed. The following constraint must be valid: __virt_to_phys(TEXTADDR) == ZRELADDR The initial part of the kernel is carefully coded to be position independent. TEXTADDR 内核启动的虚拟地址,与ZRELADDR相对应。一般内核启动的虚拟地址为RAM的第一个bank地址加上0x8000。 TEXTADDR = PAGE_OFFSET + TEXTOFFST Virtual start address of kernel, normally PAGE_OFFSET + 0x8000.This is where the kernel image ends up. With the latest kernels, it must be located at 32768 bytes into a 128MB region. Previous kernels placed a restriction of 256MB here. TEXT_OFFSET 内核偏移地址,即内核起始位置相对于内存起始位置的偏移,对于相对于物理内存还是相对于虚拟内存都是一样的结果。在arch/arm/makefile中设定。 PHYS_OFFSET RAM第一个bank的物理起始地址,即物理内存的起始地址。 Physical start address of the first bank of RAM. PAGE_OFFSET RAM第一个bank的虚拟起始地址,即内核虚拟地址空间的起始地址。 2、小结 从上面分析可知道,linux内核被bootloader拷贝到RAM后,解压代码从ZTEXTADDR开始运行(这段代码是与位置无关的PIC)。内核被解压缩到ZREALADDR处,也就是内核启动的物理地址处。相应地,内核启动的虚拟地址被设定为TEXTADDR,满足如下条件: TEXTADDR = PAGE_OFFSET + TEXT_OFFSET 内核启动的物理地址和虚拟地址满足入下条件: ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)= virt_to_phys(TEXTADDR) 假定开发板为smdk2410,则有: 内核启动的虚拟地址 TEXTADDR = 0xC0008000 内核启动的物理地址 ZRELADDR = 0x30008000 如果直接从flash中启动还需要设置ZTEXTADDR地址。

(完整版)linux内核技术

一、教学目的 SMP、多核系统、高性能浮点处理器和新型总线等创新技术,带动操作系统不断发展。本课程使硕士生了解linux的基本原理和结构特征,提高应用现代操作系统的水平、能开发特定的内核功能、设备驱动程序和复杂应用软件的能力。 二、教学内容与要求 1掌握处理器在进程地址空间上的三种运行位置,了解内核编程不能使用C库函数和FPU,以及可能产生内存故障、核心栈溢出和四种内核竞争情形的原因。(2学时)2熟悉进程描述符的组织,进程上下文和进程状态转换,和fork,exec,wait,exit,clone,linux线程和内核线程的实现原理和应用。了解COW和避免出现孤儿进程技术。 (4小时) 3介绍支持SMP的O(1)调度,用户和内核抢占和进程上下文切换,了解优先级复算,睡眠和唤醒机制,SMP的负载均衡。(4小时) 4掌握在x86体系结构上系统调用的具体实现原理,接口参数传递,用户地址空间和核心地址空间之间的数据传输,和增加新的系统功能的方法。(2小时)5熟悉在x86体系结构上Linux中断和异常的处理原理,中断注册、共享、控制,和中断上下文的意义,中断和设备驱动程序的关系,以及设备驱动程序结构和用户接口。 (4小时) 6中断处理程序被分解为top half和bottom half的原因,介绍linux的softirq,tasklet,ksoftirqd和work queue,分析进程与top half,bottom half的竞争情形和同步。(4小时)7掌握内核同步原理和方法:原子操作,自旋锁,(读—写)信号量,完成变量,bkl,seqlock和延迟内核抢占。了解指令“路障”。(4小时) 8介绍系统时钟和硬件定时器,单处理器和多处理器上的linux计时体系结构,定时的时间插补原理,单处理器和多处理器上的时钟中断处理,动态定时器的数据结构和算法原理,定时器竞争情形,延迟函数。Time,gettimeofday,adjtimex,setitimer,alarm 的实现原理和应用。(4小时) 9熟悉进程地址空间的区和页,分配和释放物理页,物理地址与逻辑地址、虚地址之间的映射,slub分配原理和方法,高端物理内存的映射。(4小时) 10介绍VFS原理,超级块,inode结构和方法,dentry结构和方法,file结构和方法,以及进程打开文件表,linux中的文件系统。(2小时) 11讲解块设备缓冲,bio结构,I/O请求队列,和有最终期限的块I/O调度算法。(2小时) 12熟悉进程地址空间的分区,mm_struct结构,vm_area_struct结构和操作,,进程的页表文件映射接口mmap原理和方法。(2小时) 13熟悉页cache和radix_tree,缓冲区cache,和pdflush内核线程原理。(2小时) 三、教学方式 教学方式:课堂讲授 考试方式:堂上考试、考查都采用笔试。

Linux启动全过程-由bootloader到fs

Linux启动过程 许多人对Linux的启动过程感到很神秘,因为所有的启动信息都在屏幕上一闪而过。其实Linux的启动过程并不象启动信息所显示的那样复杂,它主要分成两个阶段: 1.启动内核。在这个阶段,内核装入内存并在初始化每个设备驱动器时打印信息。 2.执行程序init。装入内核并初始化设备后,运行init程序。init程序处理所有程序的启动, 包括重要系统精灵程序和其它指定在启动时装入的软件。 下面以Red Hat为例简单介绍一下Linux的启动过程。 一、启动内核 首先介绍启动内核部分。电脑启动时,BIOS装载MBR,然后从当前活动分区启动,LILO获得引导过程的控制权后,会显示LILO提示符。此时如果用户不进行任何操作,LILO将在等待制定时间后自动引导默认的操作系统,而如果在此期间按下TAB键,则可以看到一个可引导的操作系统列表,选择相应的操作系统名称就能进入相应的操作系统。当用户选择启动LINUX操作系统时,LILO就会根据事先设置好的信息从ROOT文件系统所在的分区读取LINUX映象,然后装入内核映象并将控制权交给LINUX内核。LINUX内核获得控制权后,以如下步骤继续引导系统: 1. LINUX内核一般是压缩保存的,因此,它首先要进行自身的解压缩。内核映象前面的一些代码完成解压缩。 2. 如果系统中安装有可支持特殊文本模式的、且LINUX可识别的SVGA卡,LINUX会提示用户选择适当的文本显示模式。但如果在内核的编译过程中预先设置了文本模式,则不会提示选择显示模式。该显示模式可通过LILO或RDEV工具程序设置。 3. 内核接下来检测其他的硬件设备,例如硬盘、软盘和网卡等,并对相应的设备驱动程序进行配置。这时,显示器上出现内核运行输出的一些硬件信息。 4. 接下来,内核装载ROOT文件系统。ROOT文件系统的位置可在编译内核时指定,也可通过LILO 或RDEV指定。文件系统的类型可自动检测。如果由于某些原因装载失败,则内核启动失败,最终会终止系统。 二、执行init程序 其次介绍init程序,利用init程序可以方便地定制启动其间装入哪些程序。init的任务是启动新进程和退出时重新启动其它进程。例如,在大多数Linux系统中,启动时最初装入六个虚拟的控制台进程,退出控制台窗口时,进程死亡,然后init启动新的虚拟登录控制台,因而总是提供六个虚拟登陆控控制台进程。控制init程序操作的规则存放在文件/etc/inittab中。Red Hat Linux缺省的inittab文件如下:# #inittab This file describes how the INIT process should set up the system in a certain #run-level. # # #Default runlevel.The runlevels used by RHS are: #0-halt(Do NOT set initdefault to this) #1-Single user mode #2-Multiuser,without NFS(the same as 3,if you do not have networking) #3-Full multiuser mode #4-unused #5-X11 #6-reboot(Do NOT set initdefault to this)

Linux内核分析-网络[五]:网桥

看完了路由表,重新回到netif_receive_skb ()函数,在提交给上层协议处理前,会执行下面一句,这就是网桥的相关操作,也是这篇要讲解的容。 view plaincopy to clipboardprint? 1. s kb = handle_bridge(skb, &pt_prev, &ret, orig_dev); 网桥可以简单理解为交换机,以下图为例,一台linux机器可以看作网桥和路由的结合,网桥将物理上的两个局域网LAN1、LAN2当作一个局域网处理,路由连接了两个子网1.0和2.0。从eth0和eth1网卡收到的报文在Bridge模块中会被处理成是由Bridge收到的,因此Bridge也相当于一个虚拟网卡。 STP五种状态 DISABLED BLOCKING LISTENING LEARNING FORWARDING 创建新的网桥br_add_bridge [net\bridge\br_if.c] 当使用SIOCBRADDBR调用ioctl时,会创建新的网桥br_add_bridge。 首先是创建新的网桥: view plaincopy to clipboardprint?

1. d ev = new_bridge_dev(net, name); 然后设置dev->dev.type为br_type,而br_type是个全局变量,只初始化了一个名字变量 view plaincopy to clipboardprint? 1. S ET_NETDEV_DEVTYPE(dev, &br_type); 2. s tatic struct device_type br_type = { 3. .name = "bridge", 4. }; 然后注册新创建的设备dev,网桥就相当一个虚拟网卡设备,注册过的设备用ifconfig 就可查看到: view plaincopy to clipboardprint? 1. r et = register_netdevice(dev); 最后在sysfs文件系统中也创建相应项,便于查看和管理: view plaincopy to clipboardprint? 1. r et = br_sysfs_addbr(dev); 将端口加入网桥br_add_if() [net\bridge\br_if.c] 当使用SIOCBRADDIF调用ioctl时,会向网卡加入新的端口br_add_if。 创建新的net_bridge_port p,会从br->port_list中分配一个未用的port_no,p->br会指向br,p->state设为BR_STATE_DISABLED。这里的p实际代表的就是网卡设备。 view plaincopy to clipboardprint? 1. p = new_nbp(br, dev); 将新创建的p加入CAM表中,CAM表是用来记录mac地址与物理端口的对应关系;而刚刚创建了p,因此也要加入CAM表中,并且该表项应是local的[关系如下图],可以看到,CAM表在实现中作为net_bridge的hash表,以addr作为hash值,链入 net_bridge_fdb_entry,再由它的dst指向net_bridge_port。

《边学边干-Linux内核指导》学习笔记一

写在前面的话:刚毕业,找的工作是嵌入式开发,在学校自学了几年Windows平台下的C/C++开发,可现在工作是在Linux平台下,所以一切都要从头开始(当然语言基础还是有用的)。从操作系统应用开始学,再到开发,经过几个月的学习,现在基本能胜任工作。在这个几个月之中,自己感觉大学里面荒废了太多时间。在学校时,总是认为学的东西没用(我想大概现在很多在校的学生还是这么认为吧),到现在才感觉自己基本功不够扎实。所以,决定好好研究一下linux内核,一方面是为了对操作系统有更深层次的理解,另一方面在研究内核的同时,学习linux底层编程。我用的教材是浙江大学出版社《Linux内核情景分析》上下册还有就是现在看的这本。希望有相同兴趣的朋友多多交流。 第一章了解Linux内核 这一章是对Linux内核的总体概括,我把我学会以及自己在实践过程中的理解并且觉得比较容易用的到东西分别叙述如下: 一、重新编译内核 1. 首先要确认你系统里面是否有linux的源代码,在Linux安装的时候有选项是否安装源代码。如果你在安装时没有选,不用担心,你可以到https://www.360docs.net/doc/a69649293.html,/pub/linux/kernel 去下载你的系统对应的内核源代码。 2. 下载以后当然是将源代码包解压到你所要放的目录下,书上用的是下面两个命令: #mv linux-2.4…. your directory #tar zxvf linux-2.4… 3.配置内核 配置内核的过程可以分为以下几步: a.清除目录下所有配置文件和先前生成核心时产生的中间文件,在你的源代码目录执行如下命令: #make mrproper 注意:Kernel HOWTO对上面这个命令是这样解释的: `make mrproper' 将会做更广泛的`清除' 工作.这个动作有时候是必须的,所以你可能会希望在每次修补的时候执行它.`make mrproper' 还会将你的配置文件杀掉,所以如果你认为它重要的话应该先做一备份(在.config). b.对内核进行配置,有以下几种方法可供选择: #make config(基于文本的传统界面) #make menuconfig(基于文本的选单式配置界面,书中推荐的方式) #make xconfig(基于图形的配置界面) #make oldconfig(用于在原来配置的基础上作些小的修改) 注:我在我的机器上(redhat 9)执行make oldconfig这个命令的时候,系统根本没有让我配置内核,而是自动执行了配置,那我要怎么样用这个命令来配置内核呢,希望知道的朋友可以告诉我。在Kernel Howto中对这个命令的解释是: make oldconfig' 会尝试由一旧的配置文件来配置你的核心.它会为你执行`make config'。如果你还未曾编译过核心或没有旧的配置文件,那么你可能不该做这个,因为你几乎确定会更改缺省的配置。 c.对每一个配置项,用户有三种选择: “Y”——将该功能编译进内核; “N”——不将该功能编译进内核; “M”——将该功能编译成可以在需要时动态插入到内核中的模块。 d.编译内核和模块 接下来需要运行下面两条命令:

相关文档
最新文档