`
cbi339gd
  • 浏览: 9267 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

Linux 2.6 版内核引导过程学习

 
阅读更多

Linux 2.6 版内核引导过程学习
2010年12月29日
  Linux 2.6 版内核引导过程浅谈 阅读linux内核源码的过程,是一个将计算机学科的所有理论课程与实际的产品紧密联系起来的过程。阅读类UNIX的源码,在国外操作系统课程的教学过程中,曾经伴随了一代计算机人才的培养。遗憾的是,我学了几年计算机专业的课程,直到最近才感觉有了一定的积累,进而阅读了linux内核的源码,切实感受到了作为计算机科学与技术精髓的操作系统的魅力所在。
  1.Linux和linux内核
  很多linux的用户一直争论和比较各种linux发行版的易用性,但是大家都认同一点:linux,通常是指linux内核,而且尽管软件包管理方式不同、图形界面不同以及上层应用软件也相应不同,但却都具有相同的内核--linux内核。内核是一个操作系统最核心的部分,相当于汽车发动机、PC机处理器在各自系统中的核心地位。
  由于我内核的阅读才刚刚起步。所以仅仅针采用x86处理器的PC机器的加电启动直到linux内核程序被成功加载的过程做一点源码阅读级别的阐述。最新的内核是2.6版本的,2.6内核与上一个版本2.4内核的差异还是较大的,这不仅仅表现在源码的组织上,更在于新内核所采用的诸多新的高性能算法上。
  2.4内核的资料在网上众多,成为我读懂内核源码的最为重要的参考。试图通过寻找内核开发组织的开发者邮件列表以求了解2.4和2.6差异的过程也是艰辛的。时间所限,我并未采取这一方法,而是采取了阅读2.4资料,对比2.6新版源码的策略。
  在搜索并阅读相关资料以求搞清楚linux内核到底是如何运转起来的过程中,我决定借这次课程论文的机会,求同存异,对2.6版本的内核做一点阐述。
  linux内核相当复杂。且不说作为操作系统的内核这样的程序,编写源码需要各类技巧,仅仅是所涉及到的知识,分门别类的就有许多许多。可见,内核的阅读是个长期和反复的过程。也正是如此,一方面,linux牵涉到了计算机领域几乎所有的知识及其实际运用的过程,内核的阅读彰显着迷人的魅力;另一方面,linux内核在操作系统领域的应用如此广泛,以至于广大的嵌入式系统开发者都选择了在某种程度上深入学习linux内核。
  2. X86 IBM PC机的启动过程
  由于准备写这篇文章的时候,最新的稳定版的内核是2.6.35.4,鉴于学习要面向新技术的策略,源码采用了这个最新的版本。
  我对linux内核上层软件所运行的intel x86处理器相关的内存管理、中断处理的过程并不太精通,这也限制了我目前对源码的理解。当然,最为重要的是,是因为机器启动和内核启动的过程中,大量的代码都是纯AT&T汇编(.S文件)写成的,尽管汇编都是直来直去,但面向实际运用的汇编程序也确实是一个难关,何况要想深入理解,还需要大量的intel x86处理器的知识。
  一般说来,运行linux 内核的PC计算机的启动过程是这样的:
  power on -> bios -> bootloader--->kernel boot --->系统初始化(main.c init.c)--->运行应用程序
  3. Linux内核启动协议
  阅读文档\linux-2.6.35\Documentation\x86\boot.txt
  现代支持bzImage的内核的内存布局
  程序段地址是由grub的大小来决定的。地址x应该在bootloader所允许的范围内,尽可能的低。
  启动过程的内存地址分配情况:
  |  保护模式的内核 |      
  100000  +------------------------+ |  I/O memory hole |
  0A0000+------------------------+ |  为 BIOS保留 |Leave as much as possible unused ~                        ~ |  命令行 |(Can also be below the X+10000 mark)
  X+10000+------------------------+ |  栈/堆 |栈/堆空间是给内核实模式代码用的
  X+08000+------------------------+ |  Kernel setup |setup代码运行在内核的实模式下 |  Kernel boot 扇区 |The kernel legacy boot sector.
  X       +------------------------+ |  Boot loader |
  001000+------------------------+ |  Reserved for MBR/BIOS |
  000800+------------------------+ |  Typically used by MBR |
  000600+------------------------+  |  BIOS use only |
  000000+------------------------+
  传统上支持image和zimage的内存布局如下(2.4内核就是这样的布局):
  |             |
  0A0000    +------------------------+
  |  Reserved for BIOS     |    Do not use.  Reserved for BIOS EBDA.
  09A000    +------------------------+
  |  Command line         |
  |  Stack/heap         |    For use by the kernel real-mode code. 
  098000    +------------------------+
  |  Kernel setup         |    The kernel real-mode code. 
  090200    +------------------------+ |  Kernel boot sector    
  |    The kernel legacy boot sector. 
  090000    +------------------------+
  |  Protected-mode kernel |    The bulk of the kernel image.
  010000    +------------------------+
  |  Boot loader         |    stage1 :
  grub读取磁盘第一个512字节(硬盘的 0 道 0 面 1 扇区)被称为MBR(主引导记录),也称为bootsect)。MBR由一部分bootloader的引导代码、分区表和魔数三部分组成。
  stage1_5 :
  识别各种不同的文件系统格式。这使得grub识别到文件系统。
  stage2 :
  加载系统引导菜单,加载vmlinuz和initrd
  6. 内核加载过程     启动过程是和体系结构相关的,对于x86体系结构,对于2.6的内核,可以分为以下过程:
  1) BOIS选择启动设备。 
  2)从启动设备装载bootsector,。 
  3)Bootsector中的grub程序装载bzImage(包含有setup、解压缩程序和内核映像)。 
  4)在保护模式下解压内核。 
  5)汇编代码执行低级初始化(主要是对硬件如CPU和内存的初始化)。 
  6)执行上层C语言的初始化。
  bootsect.o setup.o 解压缩程序misc.o以及内核镜像vmlinuz被压缩成bzImage文件.linux 2.6中,bootsect.S和setup.S被整合为header.S。
  注 :ELF可重定位object文件(.o)  静态链接库文件.a),这是和启动密切相关的两个文件。因为.o和.a文件被链接成为可执行文件vmlinux。
  bzImage的文件构成图如下:
  vmlinuz构成:
  1. 第一个512字节  (以前是在arch/i386/boot/bootsect.S)
  2. 第二个一段代码,若干不多个512字节 (以前是在arch/i386/boot/setup.S)
  3. 保护模式下的内核代码(在\arch\x86\boot\main.c)
  Vmlinuz文件
  vmlinux是采用linux所支持的可执行文件格式的包含有linux内核的静态链接的可执行文件,传统上,vmlinux被称为可引导的内核镜像。vmlinuz是vmlinux的压缩文件。
  bzImage文件
  使用make bzImage 命令编译内核源代码,可以得到采用zlib算法压缩的zImage文件,即big zImage文件。老的zImage解压缩内核到低端内存,bzImage解压缩内核到高端内存(1M(0x100000)以上),在保护模式下执行。
  bzImage文件包含有bootsect.o + setup.o + misc.o + piggy.o.
  Initrd文件
  initrd是initialized ram disk 的意思。主要用于加载硬件驱动模块,辅助内核的启动。
  header.S
  D:\linux-2.6.35\arch\x86\boot\header.S 部分代码:
  第一部分定义了3个节,
  .bstext,
  .bsdata,
  .header,
  这3个节共同构成了vmlinuz的第一个512字节
  BOOTSEG= 0x07C0
  SYSSEG= 0x1000
  ljmp$BOOTSEG, $start2
  start2: movw%cs, %ax movw%ax, %ds movw%ax, %es movw%ax, %ss xorw%sp, %sp sti cld movw$bugger_off_msg, %si
  bugger_off_msg: .ascii"Direct booting from floppy is no longer supported.\r\n" .ascii"Please use a boot loader program instead.\r\n" .ascii"\n" .ascii"Remove disk and press any key to reboot . . .\r\n" .byte0
  下面设置内核的属性,setup的过程需要。这些属性是: .section ".header", "a" .globlhdr
  hdr:
  setup_sects:.byte 0/* Filled in by build.c */
  root_flags:.word ROOT_RDONLY
  syssize:.long 0/* Filled in by build.c */
  ram_size:.word 0/* 已经不用了 */
  vid_mode:.word SVGA_MODE
  root_dev:.word 0/* Filled in by build.c */
  boot_flag:.word 0xAA55
  header.S的第二部分,作用如同以前的setup.S
  略。
  最后,
  # Jump to C code (should not return) calllmain 调用main.c head_32.S arch/x86/boot/compressed/head_32.S  是汇编写成的32位启动代码。
  其前身是linux/boot/head.S文件。
  startup发生在在绝对地址0x00001000处的。
  head_32.s调用misc.c中的decompress_kernel()函数,将内核vmlinuz解压到0x100000处。
  head_32.S 部分源码
  /*
  * Do the decompression, and jump to the new kernel..
  */ lealz_extract_offset_negative(%ebx), %ebp /* push arguments for decompress_kernel: */ pushl%ebp/* output address */ pushl$z_input_len/* input_len */ lealinput_data(%ebx), %eax pushl%eax/* input_data */ lealboot_heap(%ebx), %eax pushl%eax/* heap area */ pushl%esi/* real mode pointer */ calldecompress_kernel addl$20, %esp
  零号页面也就是这个
  /* The so-called "zeropage" */
  struct boot_params { struct screen_info screen_info;/* 0x000 */ struct apm_bios_info apm_bios_info;/* 0x040 */ __u8  _pad2[4];/* 0x054 */ __u64  tboot_addr;/* 0x058 */ struct ist_info ist_info;/* 0x060 */ __u8  _pad3[16];/* 0x070 */ __u8  hd0_info[16];/* obsolete! *//* 0x080 */ __u8  hd1_info[16];/* obsolete! *//* 0x090 */ struct sys_desc_table sys_desc_table;/* 0x0a0 */ __u8  _pad4[144];/* 0x0b0 */ struct edid_info edid_info;/* 0x140 */ struct efi_info efi_info;/* 0x1c0 */ __u32 alt_mem_k;/* 0x1e0 */ __u32 scratch;/* Scratch field! *//* 0x1e4 */ __u8  e820_entries;/* 0x1e8 */ __u8  eddbuf_entries;/* 0x1e9 */ __u8  edd_mbr_sig_buf_entries;/* 0x1ea */ __u8  _pad6[6];/* 0x1eb */ struct setup_header hdr;    /* setup header *//* 0x1f1 */ __u8  _pad7[0x290-0x1f1-sizeof(struct setup_header)]; __u32 edd_mbr_sig_buffer[EDD_MBR_SIG_MAX];/* 0x290 */ struct e820entry e820_map[E820MAX];/* 0x2d0 */ __u8  _pad8[48];/* 0xcd0 */ struct edd_info eddbuf[EDDMAXNR];/* 0xd00 */ __u8  _pad9[276];/* 0xeec */
  } __attribute__((packed));
  enum { X86_SUBARCH_PC = 0, X86_SUBARCH_LGUEST, X86_SUBARCH_XEN, X86_SUBARCH_MRST, X86_NR_SUBARCHS,
  };
  Misc.c部分源码
  D:\linux-2.6.35.4\arch\x86\boot\compressed\misc.c
  做字符串显示用的scroll和putstr好难懂啊:
  static void scroll(void)   { int i; memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2); for (i = (lines - 1) * cols * 2; i screen_info.orig_video_mode == 0 &&     lines == 0 && cols == 0) return; x = real_mode->screen_info.orig_x; y = real_mode->screen_info.orig_y; while ((c = *s++) != '\0') { if (c == '\n') { x = 0; if (++y >= lines) { scroll(); y--; } } else { vidmem[(x + cols * y) * 2] = c; if (++x >= cols) { x = 0; if (++y >= lines) { scroll(); y--; } } }//end else }//end while real_mode->screen_info.orig_x = x; real_mode->screen_info.orig_y = y; pos = (x + cols * y) * 2;/* Update cursor position */ outb(14, vidport); outb(0xff & (pos >> 9), vidport+1); outb(15, vidport); outb(0xff & (pos >> 1), vidport+1); } Misc.c中这段用于解压缩内核的代码,需要具备专业知识才能写出来:
  asmlinkage void decompress_kernel(void *rmode, memptr heap,   unsigned char *input_data,   unsigned long input_len,   unsigned char *output)
  { real_mode = rmode; if (real_mode->hdr.loadflags & QUIET_FLAG) quiet = 1; if (real_mode->screen_info.orig_video_mode == 7) { vidmem = (char *) 0xb0000; vidport = 0x3b4; } else { vidmem = (char *) 0xb8000; vidport = 0x3d4; } lines = real_mode->screen_info.orig_video_lines; cols = real_mode->screen_info.orig_video_cols; free_mem_ptr     = heap;/* Heap */ free_mem_end_ptr = heap + BOOT_HEAP_SIZE; if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1)) error("Destination address inappropriately aligned");
  #ifdef CONFIG_X86_64 if (heap > 0x3fffffffffffUL) error("Destination address too large");
  #else if (heap > ((-__PAGE_OFFSET-(512关系以及具体代码的详细分析有待于今后补充.
  8.
  参考文献:
  参考了好多好多
分享到:
评论

相关推荐

    Linux2.6内核标准教程(共计8-- 第1个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux2.6内核标准教程(共计8--第8个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux2.6内核标准教程(共计8--第6个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux2.6内核标准教程(共计8--第3个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux2.6内核标准教程(共计8--第7个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux 2.6内核标准教程(部分)

    全书共8章,首先讲解Linux系统的引导过程;然后对Linux内核的3大核心模块——内存管理、进程管理、中断和异常处理进行了深入的分析;在此基础上,对时间度量、系统调用进行了分析和讨论;最后讲解了Linux内核中常见...

    linux 2.6内核标准教程

    全书共8章,首先讲解Linux系统的引导过程;然后对Linux内核的3大核心模块——内存管理、进程管理、中断和异常处理进行了深入的分析;在此基础上,对时间度量、系统调用进行了分析和讨论;最后讲解了Linux内核中常见...

    linux 2.6内核标准 part3/3

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学习操作系统的参考书。Linux内核是Linux操作系统中最核心的部分,用于实现对硬件...

    Linux 2.6内核标准 Part4 (Final)

    全书共8章,首先讲解Linux系统的引导过程;然后对Linux内核的3大核心模块--内存管理、进程管理、中断和异常处理进行了深入的分析;在此基础上,对时间度量、系统调用进行了分析和讨论;最后讲解了Linux内核中常见的...

    Linux2.6内核标准教程(共计8--第2个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux2.6内核标准教程(共计8--第4个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux2.6内核标准教程(共计8--第5个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux 2.6内核标准 Part3

    全书共8章,首先讲解Linux系统的引导过程;然后对Linux内核的3大核心模块--内存管理、进程管理、中断和异常处理进行了深入的分析;在此基础上,对时间度量、系统调用进行了分析和讨论;最后讲解了Linux内核中常见的...

    Linux 2.6内核标准 Part2

    全书共8章,首先讲解Linux系统的引导过程;然后对Linux内核的3大核心模块--内存管理、进程管理、中断和异常处理进行了深入的分析;在此基础上,对时间度量、系统调用进行了分析和讨论;最后讲解了Linux内核中常见的...

    Linux 2.6内核标准 Part1

    全书共8章,首先讲解Linux系统的引导过程;然后对Linux内核的3大核心模块--内存管理、进程管理、中断和异常处理进行了深入的分析;在此基础上,对时间度量、系统调用进行了分析和讨论;最后讲解了Linux内核中常见的...

    zImage linux kernel 2.6.22.14

    这个是linux kernel 2.6.22.14的内核映像;支持s3c2410 平台。cs8900a网卡。内置sharp lcd 驱动程序支持CH7004C/5C RGB输出,及UDA1341声卡驱动!支持 nfs 文件及Yaffs2。支持qtopia core 4.3.2。内核暂时不支持MMC/...

    linux内核引导参数介绍

    linux2.6的内核引导参数介绍,是个不错参考文件

    LINUX 2_6内核标准 part1/3

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学习操作系统的参考书。Linux内核是Linux操作系统中最核心的部分,用于实现对硬件...

    LINUX 2_6内核标准 part2/3

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学习操作系统的参考书。Linux内核是Linux操作系统中最核心的部分,用于实现对硬件...

    嵌入式系统Linux内核开发实战指南(ARM平台)(2/2)

     另外,作者根据自己反复阅读linux2.6内核源代码和linux内核参考书的笔记与心得,用很大篇幅深入剖析了linux内核的组成结构以及各组件的实现原理,在阐述理论的同时对内核源代码进行详细注释,这样既加深了对linux...

Global site tag (gtag.js) - Google Analytics