搜档网
当前位置:搜档网 › 这是一篇对armlinux内核启动的分析

这是一篇对armlinux内核启动的分析

这是一篇对armlinux内核启动的分析
这是一篇对armlinux内核启动的分析

1.介绍 (2)

2.相关定义介绍 (3)

2.1.TEXTADDR (3)

2.2.stext (3)

2.3.swapper_pg_dir (4)

2.4.(M)pgtbl (4)

2.5.(M)krnladr (5)

2.6.https://www.sodocs.net/doc/b01376651.html,段 (5)

2.7.__proc_info_begin/__proc_info_end (6)

2.8.https://www.sodocs.net/doc/b01376651.html,段 (7)

2.9.__arch_info_begin/__arch_info_end (8)

3.代码分析 (9)

3.1.KERNEL ENTRY (9)

3.2.__arm920_setup (11)

3.3.__ret (13)

3.4.__mmap_switched (14)

3.5.__lookup_processor_type (16)

3.6.__lookup_architecture_type (16)

3.7.__create_page_tables (19)

1.介绍

这是一篇对armlinux内核启动的分析,主要是arch/arm/kernel/head-armv.S文件, head-armv.S文件是整个内核的入口,也就是说bootloader执行完毕后将跳转到head-armv.S的第一条指令,head-armv.S执行完后将跳转到start_kernel(),在head-armv.S的执行过程中也用到了其他一些文件,包括arch/arm/kernel/debug-armv.S、arch/arm/mm/proc-arm920.S等等

由于此分析基于MX1的内核启动过程,因此除了通用代码,只有定义在CONFIG_ARCH_MX1ADS下的代码和proc-arm920.S(arm920是MX1的CPU)的代码被分析

在下面程序流程的说明中,MX1板子启动过程中的寄存器值将会用绿色字体表示出来,而对于专门针对MX1的代码则会用下划线字体表示

2.相关定义介绍

2.1.TEXTADDR

TEXTADDR是内核Image的映像地址,也是内核Image所处的虚拟地址,它在系统内核空间起始地址——通常是0xC0000000(这相对应于物理内存开始的地方)+32K的位置,也就是0xC0008000处TEXTADDR的赋值在arch/arm/Makefile文件中:(0xC0008000)

ifeq ($(CONFIG_CPU_32),y)

PROCESSOR = armv

TEXTADDR = 0xC0008000

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

endif

在内核映像之前的16K空间用来存放内核的页目录表,这就是为什么TEXTADDR要在系统要放在0xC0008000的缘故——它必须留出足够的物理空间来放页表

在head-armv.S中TEXTADDR将被检测:

#if (TEXTADDR & 0xffff) != 0x8000 //TEXTADDR必须为0xXXXX8000

#error TEXTADDR must start at 0xXXXX8000

#endif

2.2.stext

stext是TEXTADDR相对应的物理地址,由于内核空间的起始地址(0xC0000000)相对应的物理地址就是最低物理内存的地址,因此stext就是最低物理内存+32K处(虽然这并不是有内核指定,而是由bootloader指定),它的赋值在连接脚本vmlinux-armv.lds.in中实现:(0x08008000)

ENTRY(stext)

SECTIONS

{

=

TEXTADDR; //此处的映像地址为TEXTADDR

.

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

.; //stext为此处的物理地址

_stext

=

2.3.swapper_pg_dir

swapper_pg_dir是页表的映像地址,由于启动页表在内核Image之前的16K 处,因此它等于0xC0004000,它的定义在head-armv.S文件中

.globl SYMBOL_NAME(swapper_pg_dir) //设置swapper_pg_dir

-

0x4000 S YMBOL_NAME(swapper_pg_dir),

TEXTADDR

.equ

在定义init进程的mm_struct结构的宏INIT_MM中,swapper_pg_dir作为页表基址被赋给init_mm,INIT_MM定义在include/linux/sched.h文件中:

#define INIT_MM(name) \

{ \

pgd: swapper_pg_dir, \

}

2.4.(M)pgtbl

pgtbl是一个用于获得启动页表物理地址的宏,它将stext减去16K给reg,它也在head-armv.S中定义:

rambase

.macro pgtbl,

reg,

stext

\reg,

adr

#0x4000 //reg=stext-0x4000

\reg,

sub

\reg,

.endm

2.5.(M)krnladr

这个宏用于由pgtable获得内核空间的起始物理地址的所在的段(MB),将pgtable,也就是页表地址(和内核空间的起始物理地址在同一个段内)和0x000FFFFF相与,因为页表地址后12位为零,所以将其和0x000FF000相与/*

* Since the page table is closely related to the kernel start address, we

* can convert the page table base address to the base address of the section

* containing both.

*/

.macro krnladr, rd, pgtable, rambase

\pgtable,

#0x000ff000

\rd,

bic

.endm

2.6.https://www.sodocs.net/doc/b01376651.html,段

https://www.sodocs.net/doc/b01376651.html,段中存放的是各种处理器的信息,每个处理器的信息用一个proc_info_list结构来表示,这个结构在include/asm/procinfo.h文件中声明:struct proc_info_list {

unsigned int cpu_val; //处理器类型

unsigned int cpu_mask; //处理器类型掩码

unsigned long __cpu_mmu_flags; /* used by head-armv.S */ unsigned long __cpu_flush; /* used by head-armv.S */

const char *arch_name;

const char *elf_name;

unsigned int elf_hwcap;

struct proc_info_item *info;

#ifdef MULTI_CPU

struct processor *proc;

#else

void *unused;

#endif

};

虽然结构是在这里声明,但是真正的定义却是在proc-arm920.S文件的最后: .section "https://www.sodocs.net/doc/b01376651.html,", #alloc, #execinstr //声明以下代码在https://www.sodocs.net/doc/b01376651.html,段中

.type __arm920_proc_info,#object

__arm920_proc_info:

.long 0x41009200 //cpu_val

.long 0xff00fff0 //cpu_mask

.long 0x00000c1e @ mmuflags

b __arm920_setup //是的!这是一条跳转指令

.long cpu_arch_name

.long cpu_elf_name

.long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT

.long cpu_arm920_info

.long arm920_processor_functions

2.7.__proc_info_begin/__proc_info_end

__proc_info_begin是https://www.sodocs.net/doc/b01376651.html,段的起始地址,而__proc_info_end是终止地址,他们存放在head-armv.S中:

2: .long __proc_info_end

.long __proc_info_begin

在连接脚本vmlinux.lds.in文件中,他们被赋值:

.;

=

__proc_info_begin

*(https://www.sodocs.net/doc/b01376651.html,)

=

.;

__proc_info_end

2.8.https://www.sodocs.net/doc/b01376651.html,段

https://www.sodocs.net/doc/b01376651.html,段类似于https://www.sodocs.net/doc/b01376651.html,段,不过它是用来存放板子信息的,它的定义是在include/asm/mach/arch.h文件中:

struct machine_desc {

/*

* Note! The first four elements are used

* by assembler code in head-armv.S

*/

architecture

/*

number */ //板子ID

int

unsigned

nr;

unsigned int phys_ram; /* start of physical ram */ //物理内存起始地址unsigned int phys_io; /* start of physical io */ //IO空间起始地址

unsigned int io_pg_offst; /* byte offset for io

tabe

entry */

page

*

//IO空间起始地址的虚拟地址在启动页表中的偏移 ……

};

而具体MX1板子的machine_desc定义则通过宏在arch/arm/mach-XXXX/arch.c 文件(或者该目录下的其他文件)中,这些宏的定义在include/asm/mach/arch.h 文件中实现,通过这些宏定义了一个machine_desc:

MACHINE_START(MX1ADS, "Motorola MX1ADS")

MAINTAINER("WBSG SPS Motorola")

#ifdef CONFIG_ARCH_MX1ADS_SRAM

BOOT_MEM(0x12000000, 0x00200000, 0xf0200000)

#else

BOOT_MEM(0x08000000, 0x00200000, 0xf0200000)

//phys_ram=0x08000000,phys_io=0x00200000,

//io_pg_offset = ((0xf0200000)>>18)&0xfffc=0x00003c08

//右移20位获得IO空间虚拟地址在页表内偏移再乘以四(4个字节)

#endif

FIXUP(mx1ads_fixup)

MAPIO(mx1ads_map_io)

INITIRQ(mx1ads_init_irq)

MACHINE_END

2.9.__arch_info_begin/__arch_info_end

__arch_info_begin是https://www.sodocs.net/doc/b01376651.html,段的起始地址,而__arch_info_end是终止地址,他们存放在head-armv.S中:

.long __arch_info_begin

.long __arch_info_end

在连接脚本vmlinux.lds.in文件中,他们被赋值:

.;

=

__arch_info_begin

*(https://www.sodocs.net/doc/b01376651.html,)

.;

__arch_info_end

=

3.代码分析

3.1.KERNEL ENTRY

下面是整个内核Image的入口,进入后必须满足以下条件,即r0为0,r1为板子ID,MMU和D-cache关闭,这其中r1的architecture ID是由bootloader传进来的

/*

* Kernel startup entry point.

*

* The rules are:

* r0 - should be 0

* r1 - unique architecture number

* MMU - off

* I-cache - on or off

* D-cache - off

*

* See linux/arch/arm/tools/mach-types for the complete list of numbers

* for r1.

*/

".text.init",#alloc,#execinstr //以下代码属于“.text.init”段

.section

#function

.type stext,

ENTRY(stext)

// mov r12, r0

#0 // r12=0

mov r12,

#if defined(CONFIG_ARCH_MX1ADS)

mov fp, r1 @ r1 contain pointer to cmdline from bootloader

#endif //将r1中包含的内核命令行指针移到fp中

// for MX1ADS, we don't pass this from bootloader, so we'll set it here

#if defined(CONFIG_ARCH_MX1ADS)

#MACH_TYPE_MX1ADS

mov r1,

#endif //此时,r1=0x000000a0

mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode

msr cpsr_c, r0 @ and all irqs disabled

//将模式设置为svc模式,并禁止IRQ和FIQ

bl __lookup_processor_type //检查处理器类型

//如果处理器有效:

//r8 = __cpu_mmu_flags,r8 = 0x00000c1e

//r9 =处理器ID

r10指向处理器结构

invalid

//如果是无效处理器

processor?

@

#0

teq

r10,

yes,

'p' //打印“p”和出错信息

error

#'p' @

moveq r0,

__error

beq

bl __lookup_architecture_type //检查板子类型

//如果板子有效:

// r5=物理内存的起始地址,r5 = 0x08000000

// r6=IO空间的起始物理地址r6=0x00200000

// r7=IO空间虚拟地址在页表中的偏移r7=0x00003c08

invalid

architecture? //如果是无效板子#0 @

r7,

teq

yes,

'a' //打印“a”和出错信息

error

moveq r0,

#'a' @

__error

beq

bl __create_page_tables //建立页表

//此时页表建立完毕,

//r1=板子ID,

//r4=页表地址(stext-16K)

//r9=处理器ID,

//r10指向处理器结构

return

address //将返回地址存放在lr中

@

__ret

lr,

adr

initialise

processor

@

add

#12

pc,

r10,

reg)

control

@

(return

//跳转到处理器结构+12的位置,参看https://www.sodocs.net/doc/b01376651.html,段可以知

//道,这里是一条跳转指令“b __arm920_setup”,因此再

//跳转到proc-arm920.S中的__arm920_setup函数入口处

3.2.__arm920_setup

__arm920_setup函数在proc-arm920.S文件中,在页表建立起来之后,此函数进行一些开启MMU之前的初始化操作

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

__arm920_setup:

#0

mov r0,

mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4

//使无效整个I-cache和整个D-cache

mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4

//将Write Buffer中的数据写进内存

mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4

//使无效整个I-TLB和整个D-TLB

mcr p15, 0, r4, c2, c0 @ load page table pointer

//将r4(页表地址)写进c2(页表基址寄存器)mov r0, #0x1f @ Domains 0, 1 = client

mcr p15, 0, r0, c3, c0 @ load domain access register

//设置Domain0、Domain1和Domain2的访问权限

mrc p15, 0, r0, c1, c0 @ get control register v4

//将控制寄存器(control register)的值送给r0

/*

* Clear out 'unwanted' bits (then put them in if we need them)

*/

@ VI ZFRS BLDP WCAM

bic r0, r0, #0x0e00

bic r0, r0, #0x0002

bic r0, r0, #0x000c

bic r0, r0, #0x1000 @ ...0 000. .... 000.

/*

* Turn on what we want

*/

orr r0, r0, #0x0031

orr r0, r0, #0x2100 @ ..1. ...1 ..11 (1)

#ifdef CONFIG_CPU_ARM920_D_CACHE_ON

orr r0, r0, #0x0004 @ .... .... .... .1..

#endif

#ifdef CONFIG_CPU_ARM920_I_CACHE_ON

orr r0, r0, #0x1000 @ ...1 .... .... ....

#endif

//修改r0的某些位,使它的后16位为:XX1I 0001 XX11 0D01

//其中I表示根据CONFIG_CPU_ARM920_I_CACHE_ON设定

//D表示根据CONFIG_CPU_ARM920_D_CACHE_ON设定

//X表示不变,1表示置位,0表示清位

//具体含义如下:

//M(bit0)=1,打开MMU

//A(bit1)=0,关闭Alignment checking

//C(bit2)=D,D-cache打开/关闭

//W(bit3)=0,关闭Write Buffer

//P(bit4)=1,Exception handler进入32-bit模式

//D(bit5)=1,关闭32-bit address exception checking

//L(bit6)=X,选择Early Abort模式或者Late Abort模式

//B(bit7)=X,Little Endian/Big Endian模式

//S(bit8)=1,System Protection Bit

//R(bit9)=0,Rom Protection Bit

//F(bit10)=0,Implementation Defined

//Z(bit11)=0,关闭Branch prediction

//I(bit12)=I,I-cache打开/关闭

//V(bit13)=1,选择High exception vector

lr //返回,跳转到head-armv.S的__ret处

mov pc,

3.3.__ret

在proc-arm920.S中的__arm920_setup函数进行过一些启动MMU之前的初始化工作后,根据lr寄存器中的值跳转到__ret处执行,这里做了三件事:1.首先将__switch_data处的值(即__mmap_switched)作为返回值存放在lr寄存器中

2.开启MMU

3.最后返回(即跳转到__mmap_switched处)

请注意!__switch_data中保存的值(也就是__mmap_switched)是一个映像地址(也就是虚拟地址),也就是说,PC的值从此处由物理地址的值跳到内核空间(0xCXXXXXXX)

%object

.type __switch_data,

__switch_data: .long __mmap_switched

.long SYMBOL_NAME(compat)

.long SYMBOL_NAME(__bss_start)

.long SYMBOL_NAME(_end)

.long SYMBOL_NAME(processor_id)

.long SYMBOL_NAME(__machine_arch_type)

.long SYMBOL_NAME(cr_alignment)

.long SYMBOL_NAME(init_task_union)+8192

#ifdef CONFIG_ARCH_MX1ADS

.long SYMBOL_NAME(cmdline_from_bootloader)

//这是为MX1板子定义的从bootloader传来的参数地址

#endif

%function

.type __ret,

__ret: ldr lr, __switch_data //这里保存的是内核空间地址!

mcr p15, 0, r0, c1, c0 //将r0中的值送回c1,开启MMU!!

r0 //执行三次NOP操作,清空流水线

mov r0,

r0

mov r0,

r0

mov r0,

lr //跳到__mmap_switched处执行

mov pc,

3.4.__mmap_switched

__ret开启MMU之后,通过将__switch_data中保存的__map_switched的值跳转到此处执行,也就是从此处开始PC值转为0xCXXXXXXX

/*

* This code follows on after the page

* table switch and jump above.

*

* r0 = processor control register

* r1 = machine ID

* r9 = processor ID

*/

.align 5

__mmap_switched:

adr r3, __switch_data + 4 //r3=__switch_data+4

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

pointer

stack

@

=

sp

//r2=compat

//r4=bss_start=.bss段的起始地址

//r5=_end=.bss段的终止地址

//r6=processor_id=保存处理器ID的地址

//r7=__machine_arch_type=保存板子ID的地址

//r8=cr_alignment

//sp=initial_task+8192,由task_union结构可知,这是init进程的堆栈

[r2] //将r12中的值(0)存进compat

str r12,

#ifdef CONFIG_ARCH_MX1ADS

mov r12, fp @ fp/r11 gets used below (it originally contain

from

cmdline

bootloader) @

pointer

to

#endif

//在内核入口的一开始,r1中的包含指向内核命令行的指针被送到fp寄存器中,//现在将它送给r12

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

r5 //将整个.bss段清零

r4,

1: cmp

[r4],#4

strcc fp,

1b

bcc

processor

ID //保存处理器ID

Save

str r9,

@

[r6]

machine

type //保存板子ID

Save

@

str r1,

[r7]

#ifdef CONFIG_ARCH_MX1ADS

/* now save a pointer to the cmdline_from_bootloader */

adr r3, __switch_data + 32 @ cmdline_from_bootloader

//r3=__switch_data+32=cmdline_from_bootloader的地址ldmia r3, {r4} @ r4 = address of above

//r4=[r3]=cmdline_from_bootloader

str r12,

[r4] // [r4]=r12,

//将指向内核命令行的指针赋给cmdline_from_bootloader

#endif

#ifdef CONFIG_ALIGNMENT_TRAP

#2 @

...........A.

r0,

r0,

orr

#endif

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

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

b SYMBOL_NAME(start_kernel) //跳到start_kernel处执行

3.5.__lookup_processor_type

3.6.__lookup_architecture_type

__lookup_processor_type例程用于检查当前的处理器是否有效,它没有输入,使用了r5,r6,r7三个寄存器,返回r8=__cpu_mmu_flags,r9=处理器ID,r10指向处理器结构

__lookup_architecture_type用于检查当前的板子是否有效,它需要输入r1为板子ID,使用了r2, r3, r4三个寄存器,返回r5=物理内存的起始地址,r6=IO空间的起始地址,r7=IO空间虚拟地址的段号

/*

* Read processor ID register (CP#15, CR0), and look up in the linker-built

* supported processor list. Note that we can't use the absolute addresses

* for the __proc_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.

*

* Returns:

* r5, r6, r7 corrupted

* r8 = page table flags

* r9 = processor ID

* r10 = pointer to processor structure

*/

__lookup_processor_type: //__lookup_processor_type函数入口

2f // r5=__proc_info_end的物理地址,此处伪指令adr会被 adr

r5,

//编译成add r5, pc , #xx获得运行时地址

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

// r9=__proc_info_start

// r10=__proc_info_end的映像地址sub r5, r5, r10 @ convert addresses // r5=物理地址减映像地址的差

add r7, r7, r5 @ to our address space

r9,

r5 //r7=__proc_info_end对应的物理地址r10,

add

//r10=__proc_info_start对应的物理地址mrc p15, 0, r9, c0, c0 @ get processor id //r9=处理器ID

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

//

r5=cpu_val,r6=cpu_mask,r8=__cpu_mmu_flags

r8=0x00000c1e

r6=0xff00fff0,

//

r5=0x41009200,

mask

wanted

r9 @

bits

and

r6,

r6,

r6 //比较两个处理器ID是否相同,如果相同返回

r5,

teq

lr

moveq pc,

sizeof(proc_info_list)

#36 @

add

r10,

r10,

r7 //如果不同尝试下一个处理器结构

cmp

r10,

blt 1b //直到__proc_info_end

processor

unknown

@

#0

mov r10,

mov pc,

lr

/*

* Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for * more information about the __proc_info and __arch_info structures. */

2: .long __proc_info_end //https://www.sodocs.net/doc/b01376651.html,段的终止映像地址 .long __proc_info_begin //https://www.sodocs.net/doc/b01376651.html,段的起始映像地址 .long 2b //__proc_info_end的映像地址 .long __arch_info_begin //https://www.sodocs.net/doc/b01376651.html,段的起始映像地址 .long __arch_info_end //https://www.sodocs.net/doc/b01376651.html,段的终止映像地址/*

* 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:

* r2, r3, r4 corrupted

* r5 = physical start address of RAM

* r6 = physical address of IO

* r7 = byte offset into page tables for IO

*/

__lookup_architecture_type: //__lookup_architecture_type函数入口r4,

2b // r4=__proc_info_end的物理地址

adr

ldmia r4, {r2, r3, r5, r6, r7} @ throw away r2, r3

// r2=__proc_info_end,丢弃

// r3=__proc_info_start,丢弃

// r5=__proc_info_end的映像地址

// r6=__arch_info_begin

// r7=__arch_info_end

addresses

convert

// r5=物理地址减映像地址的差r5,

sub

r4,

r5 @

add r4, r6, r5 @ to our address space

r5 //r7=__proc_info_end对应的物理地址

r7,

r7,

add

//r10=__proc_info_start对应的物理地址//同__lookup_processor_type一样,将映像地址转化为物理地址

1: ldr r5, [r4] @ get machine type // r5=板子ID r5=0x000000a0 teq r5, r1 //比较两个板子ID

beq 2f //相同则跳到2

#SIZEOF_MACHINE_DESC

r4,

r4,

add

r4,

r7

cmp

blt 1b

architecture

unknown

mov r7,

#0 @

lr

mov pc,

2: ldmib r4, {r5, r6, r7} @ found, get results

// r5=物理内存的起始地址, r5=0x08000000

// r6=IO空间的起始地址r6=0x00200000

// r7=IO空间虚拟地址的段号r7=0x00003c08

lr //返回

mov pc,

3.7.__create_page_tables

__create_page_tables用来建立初始化一级页表,此时各个寄存器的情况如下:r5=物理内存的起始地址r5=0x08000000

r6=IO空间的起始地址r6=0x00200000

r7=IO空间虚拟地址的段号r7=0x00003c08

r8 = __cpu_mmu_flags r8=0x00000c1e

r9 =处理器ID

r10指向处理器结构

r0,r1,r2,r3和r4中没有有效数据

__create_page_tables:

pgtbl r4, r5 @ page table address // r4=stext-16K(即页表地址)

r4=0x08004000

//

/*

* Clear the 16K level 1 swapper page table

*/ //将页表空间(stext-16K到stext)的16K空间清零

r4 // r0 = r4 = stext-16K ,r4=0x08004000

mov r0,

#0 // r3 = 0

mov r3,

#0x4000 // r2 = r0+16K = stext,r2=0x08008000

r0,

add

r2,

1: str r3, [r0], #4 //循环清零页表空间

#4

str r3,

[r0],

[r0],

#4

str r3,

#4

[r0],

str r3,

r2

r0,

teq

1b

bne

//此时:r0 = r2 = stext ,r4 = stext-16K,r3 = 0

/*

* Create identity mapping for first MB of kernel to

* cater for the MMU enable. This identity mapping

* will be removed by paging_init()

*/

of

kernel

r5 @

start

krnladr

r2,

r4,

add r3, r8, r2 @ flags + kernel base

str r3, [r4, r2, lsr #18] @ identity mapping

//建立恒等映射(为了开启MMU前后地址空间的一致性),即:

//映射虚拟空间:0x08000000-0x08100000

详解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内核崩溃原因分析及错误跟踪技术 随着嵌入式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内核,超详细解析子系统 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内核IMQ源码实现分析

本文档的Copyleft归wwwlkk所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性,严禁用于任何商业用途。 E-mail: wwwlkk@https://www.sodocs.net/doc/b01376651.html, 来源: https://www.sodocs.net/doc/b01376651.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 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内核技术

一、教学目的 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.sodocs.net/doc/b01376651.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.编译内核和模块 接下来需要运行下面两条命令:

Linux内核启动流程分析(一)

很久以前分析的,一直在电脑的一个角落,今天发现贴出来和大家分享下。由于是word直接粘过来的有点乱,敬请谅解! S3C2410 Linux 2.6.35.7启动分析(第一阶段) arm linux 内核生成过程 1. 依据arch/arm/kernel/vmlinux.lds 生成linux内核源码根目录下的vmlinux,这个vmlinux属于未压缩, 带调试信息、符号表的最初的内核,大小约23MB; 命令:arm-linux-gnu-ld -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o init/built-in.o --start-group arch/arm/mach-s3c2410/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o drivers/built-in.o net/built-in.o --end-group .tmp_kallsyms2.o 2. 将上面的vmlinux去除调试信息、注释、符号表等内容,生成arch/arm/boot/Image,这是不带多余信息的linux内核,Image的大小约 3.2MB; 命令:arm-linux-gnu-objcopy -O binary -S vmlinux arch/arm/boot/Image 3.将 arch/arm/boot/Image 用gzip -9 压缩生成arch/arm/boot/compressed/piggy.gz大小约 1.5MB;命令:gzip -f -9 < arch/arm/boot/compressed/../Image > arch/arm/boot/compressed/piggy.gz 4. 编译arch/arm/boot/compressed/piggy.S 生成arch/arm/boot/compressed/piggy.o大小约1.5MB,这里实 际上是将piggy.gz通过piggy.S编译进piggy.o文件中。而piggy.S文件仅有6行,只是包含了文件piggy.gz; 命令:arm-linux-gnu-gcc -o arch/arm/boot/compressed/piggy.o arch/arm/boot/compressed/piggy.S 5. 依据arch/arm/boot/compressed/vmlinux.lds 将arch/arm/boot/compressed/目录下的文件head.o 、piggy.o 、misc.o链接生成arch/arm/boot/compressed/vmlinux,这个vmlinux是经过压缩且含有自解压代码的内核, 大小约1.5MB; 命 令:arm-linux-gnu-ld zreladdr=0x30008000 params_phys=0x30000100 -T arch/arm/boot/compressed/vmlinux.lds a rch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.o arch/arm/boot/compressed/misc.o -o arch/arm /boot/compressed/vmlinux

基于Linux内核编程的实验报告(Linux内核分析实验报告)

基于Linux内核编程的实验报告(Linux内核分析实验 报告) 以下是为大家整理的基于Linux内核编程的实验报告(Linux内核分析实验报告)的相关范文,本文关键词为基于,Linux,内核,编程,实验,报告,分析,,您可以从右上方搜索框检索更多相关文章,如果您觉得有用,请继续关注我们并推荐给您的好友,您可以在教育文库中查看更多范文。 Linux内核分析实验报告

实验题目:文件系统实验 实验目的:linux文件系统使用虚拟文件系统VFs作为内核文件子系统。可以安装多种 不同形式的文件系统在其中共存并协同工作。VFs对用户提供了统一的文件访问接口。本实验的要求是 (1)编写一个get_FAT_boot函数,通过系统调用或动态模块调用它可以提 取和显示出FAT文件系统盘的引导扇区信息。这些信息的格式定义在内核文件的fat_boot_sector结构体中。函数可通过系统调用或动态模块调用。 (2)编写一个get_FAT_dir函数,通过系统调用或动态模块调用它可以 返回FAT文件系统的当 前目录表,从中找出和统计空闲的目录项(文件名以0x00打头的为从未使用过目录项,以0xe5打头的为已删除的目录项),将这些空闲的目录项集中调整到目录表的前部。这些信息的格式定义在内核文件的msdos_dir_entry结构体中。 硬件环境:内存1g以上 软件环境:Linux(ubuntu)2-6实验步骤: 一:实验原理: 以实验4为蓝本,在优盘中编译并加载模块,启动测试程序,查

/proc/mydir/myfile的文件内容。从优盘得到fat文件系统的内容存在msdos_sb_info结构中,然后得到msdos_sb_info结构相应的属性值,得到实验一的数据。实验二中,得到fat文件系统第一个扇区的十六个文件信息。然后按照文件名头文字的比较方法,应用归并排序的方法,将头文件是0x00和0xe5的文件调到前面,其他的文件调到后面 二:主要数据结构说明: (1)超级块对象: 数据结构说明:一个已经安装的文件系统的安装点由超级块对象代表。 structsuper_block{... conststructsuper_operations*s_op;} (2)索引i节点对象 数据结构说明:索引i节点对象包含了内核要操作的文件的全部控制信息,对应着打开文件的i节点表。structinode{ conststructinode_operations*i_op;...} (3)目录项对象 数据结构说明:录项对象代表了文件路径名的各个部分,目录文件名和普 通文件名都属于目录项对象。structdentry{

一文详解Linux内核的栈回溯与妙用

一文详解Linux内核的栈回溯与妙用 1 前言 说起linux内核的栈回溯功能,我想这对每个Linux内核或驱动开发人员来说,太常见了。如下演示的是linux内核崩溃的一个栈回溯打印,有了这个崩溃打印我们能很快定位到在内核哪个函数崩溃,大概在函数什么位置,大大简化了问题排查过程。 网上或多或少都能找到栈回溯的一些文章,但是讲的都并不完整,没有将内核栈回溯的功能用于实际的内核、应用程序调试,这是本篇文章的核心:尽可能引导读者将栈回溯的功能用于实际项目调试,栈回溯的功能很强大。 本文详细讲解了基于mips、arm架构linux内核栈回溯原理,通过不少例子,尽可能全面给读者展示各种栈回溯的原理,期望读者理解透彻栈回溯。在这个基础上,讲解笔者近几年项目开发过程中使用linux内核栈回溯功能的几处重点应用。 1 当内核某处陷入死循环,有时运行sysrq的内核线程栈回溯功能可以排查,但并不适用所用情况,笔者实际项目遇到过。最后是在系统定时钟中断函数,对死循环线程栈回溯20多级终于找到死循环的函数。 2 当应用程序段错误,内核捕捉到崩溃,对崩溃的应用空间进程/线程栈回溯,像内核栈回溯一样,打印应用段错误进程/线程的层层函数调用关系。虽然运用core文件分析或者gdb也很简便排查应用崩溃问题,但是对于不容易复现、测试部偶先的、客户现场偶先的,这二者就很难发挥作用。还有就是如果崩溃发生在C库中,CPU的pc和lr(arm架构)寄存器指向的函数指令在C库的用户空间,很难找到应用的代码哪里调用了C库的函数。arm架构网上能找到应用层栈回溯的例子,但是编译较麻烦,代码并不容易理解,况且mips能在应用层实现吗?还是在内核实现应用程序栈回溯比较方便。 3 应用程序发生double free,运用内核的栈回溯功能,找到应用代码哪里发生了double free。double free是C库层发现并截获该事件,然后向当前进程/线程发送SIGABRT进程终止信号,后续就是内核强制清理该进程/线程。double free比应用程序段错误更麻烦,后

嵌入式linux系统地启动过程

一、分析嵌入式系统的启动过程 嵌入式系统的启动过程: 上电------->u-boot------->加载Linux内核------->挂载rootfs ---->执行应用程序 二、分析u-boot 1.什么是u-boot(是一个通用的bootloader) U-Boot,全称Universal Boot Loader,是遵循GPL条款的开放源码项目。 Universal ----------->通用的 Boot ----------------->启动,引导 Loader ----------------->加载 通用------->支持多种架构的CPU,除了支持ARM系列的处理器外,还能支持MIPS、x86、Power PC、NIOS等诸多常用系列的处理器 ------->支持多种厂家的开发板,如cortex-A8,cortex-A9,cortex-A53等不同厂 家的开发板 ------->支持多种嵌入式操作系统,U-Boot不仅仅支持嵌入式Linux系统的引导,它还支持Net BSD, Vx Works, QNX, RTEMS, ARTOS, Lynx OS, android 嵌入式操作系统。 Boot -------->完成硬件的初始化,启动硬件平台。 Loader ------->当初始化硬件结束后,加载操作系统。 2.u-boot的作用 大多数BootLoader都分为stage1和stage2两大部分,U-boot也不例外。依赖于cpu体系结构的代码(如设备初始化代码等)通常都放在stage1且可以用汇编语言来实现,而stage2则通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。 (1)Stage1:CPU(S5P6818-->Cortex-A53)的初始化,使用汇编语言编写。 如:初始化Cache、MMU、clock、中断、看门狗、DDR3、eMMC、... (2)Stage2:板级初始化,使用C语言编写。 如:uart、网卡、usb、LCD、.... (3)提供了一些工具,如进入uboot的命令行模式,使用u-boot命令 (4)加载操作系统 3.U-boot的工作模式 U-Boot的工作模式有启动加载模式和下载模式。

(完整版)Linux内核实验报告——实验4

Linux内核实验报告 实验题目:动态模块设计实验 实验目的: Linux 模块是一些可以独立于内核单独编译的内核函数和数据类型集合,是可增删的 内核部分。模块在内核启动时装载称为静态装载,在内核已经运行时装载称为动态装载。 模块可以扩充内核所期望的任何功能,但通常用于实现设备驱动程序。 通过本实验,将学习到动态模块的设计过程,以及Proc文件系统的部分知识。 硬件环境: Pentium(R) Dual-Core CPU T4400 @ 2.20GHz 软件环境: Ubuntu12.04 gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)

内核版本:3.0.24 实验步骤: 1、代码分析 模块初始化和模块卸载的函数都类同,同时读取proc文件的函数在本次实验中没有用到,所以着重描述写驱动函数。 实验A: 在这个proc函数中只是简单得输出jiffies的数值。 实验B:遍历父进程和所有进程,依然是在proc_read中,通过以下代码片段完成功能,注意在这里,我们是通过直接向系统分配的那一个page直接写入来得到的,所以每次不同的进程访问该proc文件的时候得到的结果都不一样 遍历父进程: len += sprintf(page+len,"遍历父进程\npid\tppid\tcomm\n"); while (task != &init_task){ len += sprintf(page + len,"%d\t%d\t[%s]\n",task->pid,task->parent->pid,task->comm); task = task->parent; } 遍历所有进程,通过for_each_process(task)这个宏来遍历所有进程:

ARMLinux启动过程分析_百度文库.

ARM Linux启动过程分析 一个嵌入式Linux 系统从软件角度看可以分为四个部分[1]:引导加载程序(bootloader),Linux 内核,文件系统,应用程序。 其中 bootloader是系统启动或复位以后执行的第一段代码,它主要用来初始化处理器及外设,然后调用 Linux 内核。Linux 内核在完成系统的初始化之后需要挂载某个文件系统做为根文件系统(Root Filesystem)。根文件系统是 Linux 系统的核心组成部分,它可以做为Linux 系统中文件和数据的存储区域,通常它还包括系统配置文件和运行应用软件所需要的库。应用程序可以说是嵌入式系统的“灵魂”,它所实现的功能通常就是设计该嵌入式系统所要达到的目标。如果没有应用程序的支持,任何硬件上设计精良的嵌入式系统都没有实用意义。 从以上分析我们可以看出bootloader 和Linux 内核在嵌入式系统中的关系和作用。Bootloader在运行过程中虽然具有初始化系统和执行用户输入的命令等作用,但它最根本的功能就是为了启动 Linux 内核。在嵌入式系统开发的过程中,很大一部分精力都是花在bootloader 和 Linux 内核的开发或移植上。如果能清楚的了解 bootloader 执行流程和 Linux的启动过程,将有助于明确开发过程中所需的工作,从而加速嵌入式系统的开发过程。而这正是本文的所要研究的内容。 2. Bootloader 2.1 Bootloader的概念和作用Bootloader是嵌入式系统的引导加载程序,它是系统上电后运行的第一段程序,其作用类似于 PC 机上的BIOS。在完成对系统的初始化任务之后,它会将非易失性存储器(通常是 Flash或 DOC 等)中的Linux 内核拷贝到 RAM 中去,然后跳转到内核的第一条指令处继续执行,从而启动 Linux 内核。由此可见,bootloader 和 Linux 内核有着密不可分的联系,要想清楚的了解 Linux内核的启动过程,我们必须先得认识bootloader的执行过程,这样才能对嵌入式系统的整个启过程有清晰的掌握。 2.2 Bootloader的执行过程不同的处理器上电或复位后执行的第一条指令地址并不相同,对于 ARM 处理器来说,该地址为 0x00000000。对于一般的嵌入式系统,通常把 Flash 等非易失性存储器映射到这个地址处,而 bootloader就位于该存储器的最前端,所以系统上电或复位后执行的第一段程序便是 bootloader。而因为存储 bootloader的存储器不同,bootloader的执行过程也并不相同,下面将具体分析。嵌入式系统中广泛采用的非易失性存储器通常是 Flash,而 Flash 又分为 Nor Flash 和Nand Flash 两种。它们之间的不同在于: Nor Flash 支持芯片内执行(XIP, eXecute In Place),这样代码可以在Flash上直接执行而不必拷贝到RAM中去执行。而Nand Flash并不支持XIP,所以要想执行 Nand Flash 上的代码,必须先将其拷贝到 RAM中去,然后跳到 RAM 中去执行。实际应用中的 bootloader根据所需功能的不同可以设计得很复杂,除完成基本的初始化系统和调用 Linux 内核等基本任务外,还可以执行很多用户输入的命令,比如设置 Linux 启动参数,给 Flash 分区等;也可以设计得很简单,只完成最基本的功能。但为了能达到启动Linux 内核的目的,所有的 bootloader都必须具备以下功能:BR> 1 初始化RAM 因为 Linux 内核一般都会在 RAM 中运行,所以在调用 Linux 内核之前 bootloader 必须设置和初始化 RAM,为调用

相关主题