linux内核IMQ源码实现分析
- 格式:doc
- 大小:88.50 KB
- 文档页数:9
Linux内核源码分析--内核启动之zImage⾃解压过程阅读⽬录(Content)参考:⽂档下载地址:关于内核⾃解压完毕后,执⾏start_kernel的分析,参见:内核版本:3.0.8相关⽂件:arch/arm/boot/compressed/head.Sarch/arm/boot/compressed/vmlinux.ldsarch/arm/boot/compressed/piggy.gzip这⾥仅对内核⾃解压进⾏简要分析,详细的分析可以阅读参考博客⽂档。
zImage来历顶层vmlinux ---->arch/arm/boot/Image --->arch/arm/boot/compressed/piggy.gz --->arch/arm/boot/compressed/vmlinux --->arch/arm/boot/zImage如果要分析zImage的反汇编反汇编⽂件,可将arch/arm/boot/compressed/vmlinux进⾏反汇编,arm-linux-xxx-objdump –d vmlinux > vmlinux.dis对顶层的vmlinux反汇编得到的是未压缩的内核的反汇编⽂件,这个vmlinux才是真正的Linux内核。
piggy.gz压缩⽂件的特点gzip -f -9 < Image > piggy.gz在piggy.gz的结尾四个字节表⽰的是 Image 镜像的⼤⼩,并且是以⼩端格式存放的。
下⾯我们验证⼀下:可以看到,Image的⼤⼩是6806148B,⼗六进制值就是67DA84,接下来看看piggy.gz的结尾:可以看到,确实是将0x67DA84以⼩端的格式存放在了piggy.gz的结尾四字节中了。
vmlinux.lds1: /*2: * linux/arch/arm/boot/compressed/vmlinux.lds.in3: *4: * Copyright (C) 2000 Russell King5: *6: * This program is free software; you can redistribute it and/or modify7: * it under the terms of the GNU General Public License version 2 as8: * published by the Free Software Foundation.9: */10: OUTPUT_ARCH(arm)11: ENTRY(_start)12: SECTIONS13: {14: /DISCARD/ : {15: *(.ARM.exidx*)16: *(.ARM.extab*)17: /*18: * Discard any r/w data - this produces a link error if we have any,19: * which is required for PIC decompression. Local data generates20: * GOTOFF relocations, which prevents it being relocated independently21: * of the text/got segments.22: */23: *(.data)24: }25:26: . = 0;27: _text = .;28:29: .text : {30: _start = .;31: *(.start)32: *(.text)33: *(.text.*)34: *(.fixup)35: *(.gnu.warning)36: *(.rodata)37: *(.rodata.*)38: *(.glue_7)39: *(.glue_7t)40: *(.piggydata)41: . = ALIGN(4);42: }43:44: _etext = .;45:46: _got_start = .;47: .got : { *(.got) }48: _got_end = .;49: .got.plt : { *(.got.plt) }50: _edata = .;51:52: . = ALIGN(8);53: __bss_start = .;54: .bss : { *(.bss) }55: _end = .;56:57: . = ALIGN(8); /* the stack must be 64-bit aligned */58: .stack : { *(.stack) }59:60: .stab 0 : { *(.stab) }61: .stabstr 0 : { *(.stabstr) }62: .stab.excl 0 : { *(.stab.excl) }63: .stab.exclstr 0 : { *(.stab.exclstr) }64: .stab.index 0 : { *(.stab.index) }65: .stab.indexstr 0 : { *(.stab.indexstr) }66: .comment 0 : { *(.comment) }67: }68:arch/arm/boot/compressed/head.S1: .section ".start", #alloc, #execinstr2: /*3: * 清理不同的调⽤约定4: */5: .align6: .arm @ 启动总是进⼊ARM状态7: start:8: .type start,#function9: .rept 710: mov r0, r011: .endr12: ARM( mov r0, r0 )13: ARM( b 1f )14: THUMB( adr r12, BSYM(1f) )15: THUMB( bx r12 )16: .word 0x016f2818 @ ⽤于boot loader的魔数17: .word start @ 加载/运⾏zImage的绝对地址(编译时确定), 在vmlinux.lds中可以看到,zImage的链接起始地址是018: .word _edata @ zImage结束地址,分析vmlinux.lds可以看到,_edata是 .got 段的结束地址,后⾯紧接的就是.bss段和.stack段 19: THUMB( .thumb )20: 1: mov r7, r1 @ 保存构架ID到r7(此前由bootloader放⼊r1)21: mov r8, r2 @ 保存内核启动参数地址到r8(此前由bootloader放⼊r2)22: #ifndef __ARM_ARCH_2__23: /*24: * 通过Angel调试器启动 - 必须进⼊ SVC模式且关闭FIQs/IRQs25: * (numeric definitions from angel arm.h source).26: * 如果进⼊时在user模式下,我们只需要做这些27: */28: mrs r2, cpsr @ 获取当前模式29: tst r2, #3 @ 判断是否是user模式30: bne not_angel31: mov r0, #0x17 @ angel_SWIreason_EnterSVC32: ARM( swi 0x123456 ) @ angel_SWI_ARM swi会产⽣软中断,会跳⼊中断向量表,这个向量表⽤的是bootloader的,因为在head.S中并没有建⽴新的向量表33: THUMB( svc 0xab ) @ angel_SWI_THUMB34: not_angel:35: mrs r2, cpsr @ 关闭中断36: orr r2, r2, #0xc0 @ 以保护调试器的运作关闭IRQ和FIQ37: msr cpsr_c, r238: #else39: teqp pc, #0x0c000003@ 关闭中断(此外bootloader已设置模式为SVC)40: #endifGOT表是什么?GOT(Global Offset Table)表中每⼀项都是本运⾏模块要引⽤的⼀个全局变量或函数的地址。
Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(L。
前⾯粗略分析start_kernel函数,此函数中基本上是对内存管理和各⼦系统的数据结构初始化。
在内核初始化函数start_kernel执⾏到最后,就是调⽤rest_init函数,这个函数的主要使命就是创建并启动内核线程init。
这个函数虽然意思为剩下的初始化,但是这个“剩下”的可是内容颇多,下⾯详细分析如下:1. /*2. * 我们必须确定在⼀个⾮__init函数或3. * 其他根线程(root thread)和初始化线程(init thread)间的竞态。
4. * (这种竞态可能导致start_kernel在根线程运作到cpu_idle前被free_initmem“收割”。
)5. *6. *7. * gcc-3.4 偶尔会将这个函数作为内联函数, 所以使⽤了noinline.8. */9.10. static __initdata DECLARE_COMPLETION(kthreadd_done);11.1. 定义⼀个complete变量来告诉init线程:kthreads线程已经创建完成。
2. 从前似乎不是⽤complete锁,⽽是⽤⼤内核锁。
12. static noinline void __init_refok rest_init(void)13. {14. int pid;15.16. rcu_scheduler_starting();17. /*18. * 我们必须先创建init内核线程,这样它就可以获得pid为1。
19. * 尽管如此init线程将会挂起来等待创建kthreads线程。
20. * 如果我们在创建kthreadd线程前调度它,就将会出现OOPS。
21. */22. kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);1. 创建kernel_init内核线程,内核的1号进程23. numa_default_policy();1. 设定NUMA系统的内存访问策略为默认24. pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);25.1. 创建kthreadd内核线程,它的作⽤是管理和调度其它内核线程。
Linux内核源码分析--内核启动之(1)zImageLinux内核源码分析--内核启动之(1)zImag(1)zImage e⾃解压过程(Linux-3.0ARMv7)研究内核源码和内核运⾏原理的时候,很总要的⼀点是要了解内核的初始情况,也就是要了解内核启动过程。
我在研究内核的内存管理的时候,想知道内核启动后的页表的放置,页表1......2OUTPUT_ARCH(arm)3ENTRY(_start)4SECTIONS5{6/DISCARD/:{7*(.ARM.exidx*)8*(.ARM.extab*)9/*10*Discard any r/w data-this produces a link error if we have any,11*which is required for PIC decompression.Local data generates12*GOTOFF relocations,which prevents it being relocated independently13*of the text/got segments.14*/15*(.data)16}17.=TEXT_START;18_text=.;19.text:{20_start=.;21*(.start)22*(.text)23......start t arch/arm/boot/compressed/head.S S找到这个star 我们可以在arch/arm/boot/compressed/head.bootloader r ⼊⼝,这样就可以从这⾥开始⽤代码分析的⽅法研究bootloade跳转到压缩内核映像后的⾃解压启动过程:再看到MMU设置的时候,我只研究了armv7的指令。
看这些代码,必须对ARM的MMU有⼀定的了解,建议参考ARMv7的构架⼿册和⽹上的⼀份PDF《ARM MMU中⽂详解》(就是ARM⼿册中MMU部分的翻译)24/*25*linux/arch/arm/boot/compressed/head.S26*27*Copyright(C)1996-2002Russell King28*Copyright(C)2004Hyok S.Choi(MPU support)29*30*This program is free software;you can redistribute it and/or modify31*it under the terms of the GNU General Public License version2as32*published by the Free Software Foundation.33*/34#include35/*36*调试宏37*38*注意:这些宏必须不包含那些⾮100%可重定位的代码39*任何试图这样做的结果是导致程序崩溃40*当打开调试时请选择以下⼀个使⽤41*/42#ifdef DEBUG/*调试宏-中间层*/43#if defined(CONFIG_DEBUG_ICEDCC)/*使⽤内部调试协处理器CP14*/44#if defined(CONFIG_CPU_V6)||defined(CONFIG_CPU_V6K)||defined(CONFIG_CPU_V7)45.macro loadsp,rb,tmp46.endm47.macro writeb,ch,rb48mcr p14,0,\ch,c0,c5,049.endm50#elif defined(CONFIG_CPU_XSCALE)51.macro loadsp,rb,tmp52.endm53.macro writeb,ch,rb54mcr p14,0,\ch,c8,c0,055.endm56#else57.macro loadsp,rb,tmp58.endm59.macro writeb,ch,rb60mcr p14,0,\ch,c1,c0,061.endm62#endif63#else/*使⽤串⼝作为调试通道*/64#include/*包含构架相关的的调试宏的汇编⽂件调试宏-底层*/ 65.macro writeb,ch,rb 66senduart\ch,\rb67.endm68#if defined(CONFIG_ARCH_SA1100)69.macro loadsp,rb,tmp70mov\rb,#0x80000000@physical base address71#ifdef CONFIG_DEBUG_LL_SER372add\rb,\rb,#0x00050000@Ser373#else74add\rb,\rb,#0x00010000@Ser175#endif76.endm77#elif defined(CONFIG_ARCH_S3C2410)78.macro loadsp,rb,tmp79mov\rb,#0x5000000080add\rb,\rb,#0x4000*CONFIG_S3C_LOWLEVEL_UART_PORT 81.endm82#else83.macro loadsp,rb,tmp84addruart\rb,\tmp85.endm86#endif87#endif88#endif/*DEBUG*/89/*调试宏-上层*/90.macro kputc,val/*打印字符*/91mov r0,\val92bl putc93.endm94.macro kphex,val,len/*打印⼗六进制数*/95mov r0,\val96mov r1,#\len97bl phex98.endm99.macro debug_reloc_start/*重定位内核调试宏-开始*/100#ifdef DEBUG101kputc#'\n'102kphex r6,8/*处理器id*/103kputc#':'104kphex r7,8/*构架id*/105#ifdef CONFIG_CPU_CP15106kputc#':'107mrc p15,0,r0,c1,c0108kphex r0,8/*控制寄存器*/109#endif110kputc#'\n'111kphex r5,8/*解压后的内核起始地址*/112kputc#'-'113kphex r9,8/*解压后的内核结束地址*/114kputc#'>'115kphex r4,8/*内核执⾏地址*/116kputc#'\n'117#endif118.endm119.macro debug_reloc_end/*重定位内核调试宏-结束*/ 120#ifdef DEBUG 121kphex r5,8/*内核结束地址*/122kputc#'\n'123mov r0,r4124bl memdump/*打印内核起始处256字节*/125#endif126.endm127.section".start",#alloc,#execinstr128/*129*清理不同的调⽤约定130*/131.align132.arm@启动总是进⼊ARM状态133start:134.type start,#function135.rept7136mov r0,r0137.endr138ARM(mov r0,r0)139ARM(b1f)140THUMB(adr r12,BSYM(1f))141THUMB(bx r12)142.word0x016f2818@⽤于boot loader的魔数143.word start@加载/运⾏zImage的绝对地址(编译时确定)144.word_edata@zImage结束地址145THUMB(.thumb)1461:mov r7,r1@保存构架ID到r7(此前由bootloader放⼊r1)147mov r8,r2@保存内核启动参数地址到r8(此前由bootloader放⼊r2)148#ifndef__ARM_ARCH_2__ 149/*150*通过Angel调试器启动-必须进⼊SVC模式且关闭FIQs/IRQs151*(numeric definitions from angel arm.h source).152*如果进⼊时在user模式下,我们只需要做这些153*/154mrs r2,cpsr@获取当前模式155tst r2,#3@判断是否是user模式156bne not_angel157mov r0,#0x17@angel_SWIreason_EnterSVC158ARM(swi0x123456)@angel_SWI_ARM159THUMB(svc0xab)@angel_SWI_THUMB160not_angel:161mrs r2,cpsr@关闭中断162orr r2,r2,#0xc0@以保护调试器的运作163msr cpsr_c,r2164#else165teqp pc,#0x0c000003@关闭中断(此外bootloader已设置模式为SVC)166#endif167/*168*注意⼀些缓存的刷新和其他事务可能需要在这⾥完成169*-is there an Angel SWI call for this?170*/171/*172*⼀些构架的特定代码可以在这⾥被连接器插⼊,173*但是不应使⽤r7(保存构架ID),r8(保存内核启动参数地址),and r9. 174*/ 175.text176/*177*此处确定解压后的内核映像的绝对地址(物理地址),保存于r4178*由于配置的不同可能有的结果179*(1)定义了CONFIG_AUTO_ZRELADDR180*ZRELADDR是已解压内核最终存放的物理地址181*如果AUTO_ZRELADDR被选择了,这个地址将会在运⾏是确定:182*将当pc值和0xf8000000做与操作,183*并加上TEXT_OFFSET(内核最终存放的物理地址与内存起始的偏移)184*这⾥假定zImage被放在内存开始的128MB内185*(2)没有定义CONFIG_AUTO_ZRELADDR186*直接使⽤zreladdr(此值位于arch/arm/mach-xxx/Makefile.boot⽂件确定)187*/ 188#ifdef CONFIG_AUTO_ZRELADDR189@确定内核映像地址190mov r4,pc191and r4,r4,#0xf8000000192add r4,r4,#TEXT_OFFSET193#else194ldr r4,=zreladdr195#endif196bl cache_on/*开启缓存(以及MMU)*/197restart:adr r0,LC0198ldmia r0,{r1,r2,r3,r6,r10,r11,r12}199ldr sp,[r0,#28]200/*201*我们可能运⾏在⼀个与编译时定义的不同地址上,202*所以我们必须修正变量指针203*/204sub r0,r0,r1@计算偏移量205add r6,r6,r0@重新计算_edata206add r10,r10,r0@重新获得压缩后的内核⼤⼩数据位置207/*208*内核编译系统将解压后的内核⼤⼩数据209*以⼩端格式210*附加在压缩数据的后⾯(其实是“gzip-f-9”命令的结果)211*下⾯代码的作⽤是将解压后的内核⼤⼩数据正确地放⼊r9中(避免了⼤⼩端问题)212*/213ldrb r9,[r10,#0]214ldrb lr,[r10,#1]215orr r9,r9,lr,lsl#8216ldrb lr,[r10,#2]217ldrb r10,[r10,#3]218orr r9,r9,lr,lsl#16219orr r9,r9,r10,lsl#24220/*221*下⾯代码的作⽤是将正确的当前执⾏映像的结束地址放⼊r10222*/223#ifndef CONFIG_ZBOOT_ROM224/*malloc获取的内存空间位于重定向的栈指针之上(64k max)*/225add sp,sp,r0226add r10,sp,#0x10000227#else228/*229*如果定义了ZBOOT_ROM,bss/stack是⾮可重定位的,230*但有些⼈依然可以将其放在RAM中运⾏,231*这时我们可以参考_edata.232*/233mov r10,r6234#endif235/*236*检测我们是否会发⽣⾃我覆盖的问题237*r4=解压后的内核起始地址(最终执⾏位置)238*r9=解压后内核的⼤⼩239*r10=当前执⾏映像的结束地址,包含了bss/stack/malloc空间(假设是⾮XIP执⾏的)240*我们的基本需求是: 241*(若最终执⾏位置r4在当前映像之后)r4-16k页⽬录>=r10->OK242*(若最终执⾏位置r4在当前映像之前)r4+解压后的内核⼤⼩<=当前位置(pc)->OK 243*如果上⾯的条件不满⾜,就会⾃我覆盖,必须先搬运当前映像244*/245add r10,r10,#16384246cmp r4,r10@假设最终执⾏位置r4在当前映像之后247bhs wont_overwrite248add r10,r4,r9@假设最终执⾏位置r4在当前映像之前249ARM(cmp r10,pc)@r10=解压后的内核结束地址250THUMB(mov lr,pc)251THUMB(cmp r10,lr)252bls wont_overwrite253/*254*将当前的映像重定向到解压后的内核之后(会发⽣⾃我覆盖时才执⾏,否则就被跳过)255*r6=_edata(已校正)256*r10=解压后的内核结束地址257*因为我们要把当前映像向后移动,所以我们必须由后往前复制代码,258*以防原数据和⽬标数据的重叠259*/260/*261*将解压后的内核结束地址r10扩展(reloc_code_end-restart),262*并对齐到下⼀个256B边界。
Linux内核(2.6.13.2)源代码分析苗彦超摘要:1系统启动1.1汇编代码head.S及以前设置CPU状态初值,创建进程0,建立进程堆栈:movq init_rsp(%rip), %rsp,init_rsp定义.globl init_rspinit_rsp:.quad init_thread_union+THREAD_SIZE-8即将虚地址init_thread_union+THREAD_SIZE-8作为当前进程(进程0)核心空间堆栈栈底,init_thread_union定义于文件arch/x86_64/kernel/init_task.c中:union thread_union init_thread_union __attribute__((__section__(".data.init_task"))) ={INIT_THREAD_INFO(init_task)};INIT_THREAD_INFO定义于文件include/asm-x86_64/thread_info.h中,初始化init_thread_union.task = &init_task,init_task同样定义于文件init_task.c中,初始化为:struct task_struct init_task = INIT_TASK(init_task);INIT_TASK宏在include/linux/init_task.h中定义。
全部利用编译时静态设置的初值,将进程0的控制结构设置完成,使进程0可以按普通核心进程访问。
init_task.mm = NULL; init_task.active_mm = INIT_MM(init_mm), init_m = “swapper”INIT_MM将init_mm.pgd初始化为swapper_pg_dir,即init_level4_pgt,定义与head.S中。
Linux内核⽹桥源码分析Linux⽹桥源码的实现转⾃:Linux⽹桥源码的实现1、调⽤在src/net/core/dev.c的软中断函数static void net_rx_action(struct softirq_action *h)中(line 1479)#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)if (skb->dev->br_port != NULL &&br_handle_frame_hook != NULL) {handle_bridge(skb, pt_prev);dev_put(rx_dev);continue;}#endif如果定义了⽹桥或⽹桥模块,则由handle_bridge函数处理skb->dev->br_port :接收该数据包的端⼝是⽹桥端⼝组的⼀员,如果接收当前数据包的接⼝不是⽹桥的某⼀物理端⼝,则其值为NULL;br_handle_frame_hook :定义了⽹桥处理函数这段代码将数据包进⾏转向,转向的后的处理函数是钩⼦函数br_handle_frame_hook,在此之前,handle_bridge函数还要处理⼀些其它的事情:static __inline__ int handle_bridge(struct sk_buff *skb,struct packet_type *pt_prev){int ret = NET_RX_DROP;if (pt_prev) {if (!pt_prev->data)ret = deliver_to_old_ones(pt_prev, skb, 0);else {atomic_inc(&skb->users);ret = pt_prev->func(skb, skb->dev, pt_prev);}}br_handle_frame_hook(skb);return ret;}pt_prev⽤于在共享SKB的时候提⾼效率,handle_bridge函数最后将控制权交由到了br_handle_frame_hook的⼿上。
Linux 内核解读入门针对好多Linux 爱好者对内核很有兴趣却无从下手,本文旨在介绍一种解读Linux内核源码的入门方法,而不是解说Linux复杂的内核机制。
1.核心源程序的文件组织(1)Linux核心源程序通常都安装在/usr/src/Linux下,而且它有一个非常简单的编号约定:任何偶数的核心(例如 2.0.30)都是一个稳定的发行的核心,而任何奇数的核心(例如2.1.42)都是一个开发中的核心。
本文基于稳定的2.2.5源代码,第二部分的实现平台为RedHat Linux 6.0。
(2)核心源程序的文件按树形结构进行组织,在源程序树的最上层你会看到这样一些目录:● Arch :arch子目录包括了所有和体系结构相关的核心代码。
它的每一个子目录都代表一种支持的体系结构,例如i386就是关于intel cpu及与之相兼容体系结构的子目录。
PC机一般都基于此目录;● Include: include子目录包括编译核心所需要的大部分头文件。
与平台无关的头文件在include/linux子目录下,与intel cpu相关的头文件在include/asm-i386子目录下而include/scsi目录则是有关scsi设备的头文件目录;● Init:这个目录包含核心的初始化代码(注:不是系统的引导代码),包含两个文件main.c和Version.c,这是研究核心如何工作的一个非常好的起点;● Mm :这个目录包括所有独立于cpu 体系结构的内存管理代码,如页式存储管理内存的分配和释放等,而和体系结构相关的内存管理代码则位于arch/*/mm/,例如arch/i386/mm/Fault.c;● Kernel:主要的核心代码,此目录下的文件实现了大多数Linux系统的内核函数,其中最重要的文件当属sched.c,同样,和体系结构相关的代码在arch/*/kernel 中;● Drivers:放置系统所有的设备驱动程序;每种驱动程序又各占用一个子目录,如/block下为块设备驱动程序,比如ide(ide.c)。
深入分析Linux内核源码第一章走进linux走进Linux内核分析Linux内核的意义Linux内核结构Linux内核源代码Linux内核源代码分析工具第二章Linux运行的硬件基础段机制和描述符Linux中的汇编语言第三章中断机制中断基本知识中断描述符表的初始化中断处理中断的后半部分处理机制第四章进程描述task_struct结构在内存中的存放进程组织的方式内核同步第五章进程调度时钟中断Linux的调度程序-Schedule( )进程切换第六章Linux内存管理Linux内存管理的初始化请页机制交换机制缓存和刷新机制进程的创建和执行第七章进程间通信第八章虚拟文件系统高速缓存文件系统的注册、安装与拆卸文件系统的系统调用第九章Ext2文件系统Ext2的磁盘布局和数据结构第十章模块机制概述实现机制模块的装入和卸载内核版本编写内核模块第十一章设备驱动程序设备驱动基础块设备驱动程序字符设备驱动程序第十二章网络网络协议套接字(socket)套接字缓冲区(sk_buff)网络设备接口第十三章启动系统初始化的任务Linux 的Boot Loarder进入操作系统建立init进程附录:1 Linux 内核API前言Linux内核全部源代码是一个庞大的世界,大约有200多万行,占60MB左右的空间。
因此,如何在这庞大而复杂的世界中抓住主要内容,如何找到进入Linux内部的突破口,又如何能把Linux的源代码变为自己的需要,这就是本书要探讨的内容。
首先,本书的第一章领你走入Linux的大门,让你对Linux内核的结构有一个整体的了解。
然后,第二章介绍了分析Linux源代码应具备的基本硬件知识,这是继续向Linux 内核迈进的必备条件。
中断作为操作系统中发生最频繁的一个活动,本书用一章的内容详细描述了中断在操作系统中的具体实现机制。
大家知道,操作系统中最核心的内容就是进程管理、内存管理和文件管理。
本书用大量的篇幅描述了这三部分内容,尤其对最复杂的虚拟内存管理进行了详细的分析,其中对内存初始化部分的详细描述将对嵌入式系统的开发者有所帮助。
Linux内核源码分析--zImage出生实录(Linux-3.0 ARMv7)自己移植编译过内核的朋友都知道:生成的zImage内核的位置在arch/arm/boot目录下。
但是这个映像是怎么产生的?下面简要地分析一下。
内核根目录下的vmlinux映像文件是内核Makefile的默认目标。
这个vmlinux映像的生成可以通过阅读内核Makefile文件得知,简单的说:Makefile 解析内核配置文件.config,递归到各目录下编译出.o文件,最后将其链接成vmlinux。
而这个链接成的vmlinux文件是一个包含内核代码的静态可执行ELF文件,你可以通过file命令来验证这一点。
她不能通过bootloader引导并启动,如果想要使其可引导,必须使用编译工具链中的objcopy命令把这个ELF格式的vmlinux转化为二进制格式才行。
而平常使用的zImage文件就是这个vmlinux文件经过多次的转换得到的。
现在就来仔细研究一下她的生成过程。
(1)arch/$(ARCH)/Makefile首先嵌入式中经常使用的编译目标zImage并不在顶层Makefile文件中,而在被顶层Makefile包含的arch/$(ARCH)/Makefile文件中,对于ARM处理器来说就是arch/arm/Makefile文件。
其中的部分规则如下:……# Default target when executing plain makeifeq ($(CONFIG_XIP_KERNEL),y)KBUILD_IMAGE := xipImageelseKBUILD_IMAGE := zImageendifall: $(KBUILD_IMAGE)boot := arch/arm/bootarchprepare:$(Q)$(MAKE) $(build)=arch/arm/toolsinclude/generated/mach-types.h# Convert bzImage to zImagebzImage: zImagezImage Image xipImage bootpImage uImage: vmlinux $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@……从这里可以看出,zImage的依赖是顶层vmlinux文件,下面的命令展开得到:make -f scripts/Makefile.build obj=arch/arm/boot MACHINE=arch/arm/mach-* arch/arm/boot/ zImage可以看出zImage其实是make解析arch/arm/boot目录下的Makefile文件生成的,而参数传递了目标芯片信息和目标“arch/arm/boot/zImage”。
[重点]linux源码分析linux源码分析Linux内核源代码中的C语言代码Linux 内核的主体是以 GNU的 C 语言编写的,GNU为此提供了编译工具gcc。
GNU对 C 语言本身(在 ANSI C 基础上)做了不少扩充,可能是读者尚未见到过的。
另一方面,由于内核代码,往往会用到一些在应用程序设计中不常见的语言成分或编程技巧,也许使读者感到陌生。
本书并非介绍 GNU C语言的专著,也非技术手册,所以不在这里一一列举和详细讨论这些扩充和技巧。
再说,离开具体的情景和上下文,罗列一大堆规则,对于读者恐怕也没有多大帮助。
所以,我们在这里只是对可能会影响读者阅读 Linux 内核源程序,或使读者感到困惑的一些扩充和技巧先作一些简单的介绍。
以后,随着具体的情景和代码的展开,在需要时还会结合实际加以补充。
首先,gcc 从 C++语言中吸收了“inline”和“const”。
其实,GNU 的 C 和C++是合为一体的,gcc既是 C 编译又是 C++编译,所以从 C++中吸收一些东西到 C 中是很自然的。
从功能上说,inline 函数的使用与#define 宏定义相似,但更有相对的独立性,也更安全。
使用 inline函数也有利于程序调试。
如果编译时不加优化,则这些inline 就是普通的、独立的函数,更便于调试。
调试好了以后,再采用优化重新编译一次,这些 inline函数就像宏操作一样融入了引用处的代码中,有利于提高运行效率。
由于 inline 函数的大量使用,相当一部分的代码从.c 文件移入了.h 文件中。
还有,为了支持 64 位的CPU结构(Alpha 就是 64 位的),gcc 增加了一种新的基本数据类型“longlong int”,该类型在内核代码中常常用到。
许多 C 语言都支持一些“属性描述符”(attribute),如“aligned”、“packed”等等;gcc 也支持不少这样的描述符。
这些描述符的使用等于是在 C 语言中增加了一些新的保留字。
Linux内核源码分析Linux内核在启动的时候需要一些参数,以获得当前硬件的信息或者启动所需资源在内存中的位置等等。
这些信息可以通过bootloader 传递给内核,比较常见的就是cmdline。
以前我在启动内核的时候习惯性的通过uboot传递一个cmdline给内核,没有具体的分析这个过程。
最近在分析内核启动过程的时候,重新看了一下内核启动参数的传递过程,彻底解决一下在这方面的疑惑。
一、bootloader与内核的通讯协议内核的启动参数其实不仅仅包含在了cmdline中,cmdline不过是bootloader传递给内核的信息中的一部分。
bootloader和内核的通信方式根据构架的不同而异。
对于ARM构架来说,启动相关的信息可以通过内核文档(Documentation/arm/Booting)获得。
其中介绍了bootloader与内核的通信协议,我简单总结如下:(1)数据格式:可以是标签列表(tagged list)或设备树(device tree)。
(2)存放地址:r2寄存器中存放的数据所指向的内存地址。
在我所做过的开发中,都是使用tagged list的,所以下面以标签列表为例来介绍信息从bootloader(U-boot)到内核(Linux-3.0)的传递过程。
内核文档对此的说明,翻译摘要如下:1.4a. 设置内核标签列表2.--------------------------------3.4.bootloader必须创建和初始化内核标签列表。
一个有效的标签列表以ATAG_CORE标签开始,且以ATAG_NONE标签结束。
ATAG_CORE标签可以是空的,也可以是非空。
一个空ATAG_CORE标签其 size 域设置为 '2' (0x00000002)。
ATAG_NONE标签的 size 域必须设置为 '0'。
5.6.在列表中可以保存任意数量的标签。
本文档的Copyleft归wwwlkk所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性,严禁用于任何商业用途。
E-mail: wwwlkk@来源: /?business&aid=6&un=wwwlkk#7linux2.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函数。
以上2个技术点就是IMQ的关键技术,下面是IMQ的具体流程。
(3)IMQ设备数据包重新注入协议栈流程(4)IMQ截留数据包流程(5)IMQ在软中断中及时将数据包重新注入协议栈到这里IMQ整个流程已经分析结束。
本文档的Copyleft归wwwlkk所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性,严禁用于任何商业用途。
E-mail: wwwlkk@
来源: /?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函数。
以上2个技术点就是IMQ的关键技术,下面是IMQ的具体流程。
(3)IMQ设备数据包重新注入协议栈流程
(4)IMQ截留数据包流程
(5)IMQ在软中断中及时将数据包重新注入协议栈
到这里IMQ整个流程已经分析结束。
(6)结束语
使用SmartFlow对IMQ做了测试,发现IMQ有些不稳定:
1)在低压力下会丢失几个数据包。
2)总体的性能降低很大。
所以IMQ并是不一个理想的QoS方案,所以这里对IMQ就不做更详细的分析。