LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 6073|回复: 6

Linux内核2.6.31启动过程 (x86架构)

[复制链接]
发表于 2009-9-21 19:47:15 | 显示全部楼层 |阅读模式
1. 构造调试环境

        由于bochs内建调试功能, 且支持gdb, 用它调试内核会很方便.

        1.1 构建磁盘镜像

       
  1. dd if=/dev/zero of=hd0.img count=$((63*16*100))
复制代码

                用这个命令可以构建一个50MB左右的磁盘镜像, 输出结果如下:

                100800+0 records in
                100800+0 records out
                51609600 bytes (52 MB) copied, 0.734578 s, 70.3 MB/s

                注意count必须为63*16的倍数, 否则bochs识别硬盘会有问题.

        1.2 挂载磁盘镜像

       
  1. losetup /dev/loop0 hd0.img
复制代码
                这个命令可以将文件绑定到一个loop设备. 如果/dev/loop0不存在, 可以尝试 modprobe loop.

                然后进行设备初始化:
       
  1. cfdisk -s 63 -h 16 /dev/loop0
复制代码

                只创建一个主分区就可以. 写入后, 用命令fdisk检查结果:
       
  1. fdisk -lu /dev/loop0
复制代码

                Disk /dev/loop0: 51 MB, 51609600 bytes
                16 heads, 63 sectors/track, 100 cylinders, total 100800 sectors
                Units = sectors of 1 * 512 = 512 bytes
                Disk identifier: 0x00000000

          Device Boot      Start         End      Blocks   Id  System
/dev/loop0p1              63      100799       50368+  83  Linux

                将分区1挂载到/dev/loop1.
       
  1. losetup /dev/loop1 hd0.img -o $((63*512))
复制代码

                格式化/dev/loop1为ext3格式.
       
  1. mkfs.ext3 /dev/loop1
复制代码

                在mnt下创建img目录, 做以后维护用.
       
  1. mkdir -p /mnt/img
复制代码

                将loop1挂载到/mnt/img
       
  1. mount /dev/loop1 /mnt/img/
复制代码

                安装引导程序. 因为我狂热倾向于模块化架构, 所以选择GRUB2.
                本文以grub-1.97~beta3为示例, 读者可自行安装其他的引导系统如lilo.

  1. mkdir /mnt/img/boot
  2. cp -r /usr/lib/grub/i386-pc/ /mnt/img/boot/grub
复制代码

                生成一个core.img, biosdisk负责读取磁盘, part_msdos负责处理MBR, ext2负责读取ext3分区.
       
  1. cd /mnt/img/boot/grub/
  2. grub-mkimage -o core.img biosdisk part_msdos ext2
复制代码

       
  1. ls -lh core.img
  2. -rw-r--r-- 1 root root 25K Sep 21 14:28 core.img
复制代码
                只有区区的25K.. 里面甚至还包含一个小的应急shell, 不过作用不大.

                安装grub2到(hd0), 根目录在(hd0,1)
       
  1. echo "(hd0)   /dev/loop0" > ./device.map
  2. grub-setup -m ./device.map -d /mnt/img/boot/grub/ -r '(hd0,1)' '(hd0)'
复制代码

                检查一下安装成果:
       
  1. hexdump -C /dev/loop0 | less
复制代码

                如果你能看到:

       
  1. 00000180  7d e8 2e 00 cd 18 eb fe  47 52 55 42 20 00 47 65  |}.......GRUB .Ge|
  2. 00000190  6f 6d 00 48 61 72 64 20  44 69 73 6b 00 52 65 61  |om.Hard Disk.Rea|
复制代码

                就说明安装成功.

                清理一下.
       
  1. cd
  2. umount /mnt/img
  3. losetup -d /dev/loop1
  4. losetup -d /dev/loop0
复制代码


        1.3 启动测试.               
                给上面的hd0.img配一个bochsrc文件, 可以拿bochs示例dlxlinux的配置文件来改, 只需将硬盘换为:

       
  1. ata0-master: type=disk, path="hd0.img", cylinders=100, heads=16, spt=63
复制代码

                然后启动Bochs, 顺利的话, 你能看到传说中的grub2 shell. 见附件pic-1.3.png


2. 从启动到保护模式.

        为我们的bochs虚拟机编译一个内核. 不必太复杂, 目前我们只关心启动部分.

        2.1 安装内核
               
                退出bochs, 挂载hd0.img:
       
  1. mount hd0.img /mnt/img/ -o loop,offset=$((63*512))
复制代码
       
                拷贝bzImage.
       
  1. cp /usr/src/linux/arch/i386/boot/bzImage /mnt/img/boot/vmlinuz-2.6.31
复制代码

                这两个步骤可以放到内核的Makefile中, 以后每次编译完成后, 自动更新到hd0.img里.

                然后将下列配置写到/mnt/img/boot/grub/grub.cfg
       
  1. # Timeout for menu
  2. set timeout=10
  3. # Set default boot entry as Entry 0
  4. set default=0
  5. # Entry 0 - Load Linux kernel
  6. menuentry "Linux-2.6.31" {
  7.         set root=(hd0,1)
  8.         linux /boot/vmlinuz-2.6.31 root=/dev/hda1
  9. }
复制代码

                卸载/mnt/img, 启动测试一下, 顺利的话, 你能看到一个panic. 见图 pic-2.1.png.


        2.2 从loader到内核

                先测试一下bochs的调试能力. bochs以调试模式启动后, 会自动停在BIOS中Reset代码, 地址为0xFFFFFFF0. 我们在bochs console里输入:
       
  1. pb 0x00007c00
复制代码
       
                在0x7c00设置一个断点, 这正式GRUB2 MBR代码的入口点. GRUB2负责加载自己的core.img, 在core.img里读取hd0, 解析MBR里的分区信息, 并利用ext2模块找到配置文件, 显示主界面,

并加载用户的选择内核文件(vmlinuz-xxx).

                loader和内核之间的协议, 在内核源码Document/x86/boot.txt里有详尽的描述.

                内核文件包括实模式代码和保护模式代码. loader读取内核文件的头部信息, 从而得知实模式和保护模式代码的大小. 保护模式部分被加载到0x100000(1MB), 实模式启动代码和数据/

堆栈段位置可以重定位在0x10000开始的低端内存的任何位置.

        2.3 内核是怎样链成的...
               
                首先编译内核的保护模式代码, 生成源码根目录的vmlinux. 这是一个elf格式的文件, 可以用readelf查看.
       
  1. readelf -a /usr/src/linux/vmlinux | less
复制代码

                这个文件包含所有的符合信息, 容量巨大.
       
  1. ls -lh /usr/src/linux/vmlinux
  2. -rwxr-xr-x 1 root root 4.8M Sep 10 17:28 /usr/src/linux/vmlinux
复制代码
                文件内容会在后面章节细述.

                接着vmlinux洗净压缩. 这个工作在 arch/x86/boot/compressed目录进行. 读Makefile可以看到先进行objcopy操作, 在compressed目录下生成vmlinux.bin.
       
  1. ls -lh /usr/src/linux/arch/x86/boot/compressed/vmlinux.bin
  2. -rwxr-xr-x 1 root root 3.7M Sep 10 17:28 /usr/src/linux/arch/x86/boot/compressed/vmlinux.bin
复制代码
                因为去掉了调试信息, 文件稍小了一些.

                接着根据用户的选择进行压缩, 目前支持gz, bz2, lzma三种方式. 我用默认方式, 生成vimlinux.bin.gz.
       
  1. #ls -lh /usr/src/linux/arch/x86/boot/compressed/vmlinux.bin.gz
  2. -rw-r--r-- 1 root root 1.9M Sep 10 17:28 /usr/src/linux/arch/x86/boot/compressed/vmlinux.bin.gz
复制代码
                可以看到也有近50%的压缩率.

                紧接着加入自解压部分, 生成新的vmlinux.
       
  1. ls -lh /usr/src/linux/arch/x86/boot/compressed/vmlinux
  2. -rwxr-xr-x 1 root root 2.0M Sep 10 17:28 /usr/src/linux/arch/x86/boot/compressed/vmlinux
复制代码
                这也是一个elf文件, 可以用readelf查看.


                接着工作转移到boot目录, 对上面的vmlinux进行strip操作, 生成二进制格式的vmlinux.bin. 这里面包括全部的包含模式代码, 启动时第一条语句会被加载到0x100000(1MB).
                然后编译实模式代码, 包括已无用的引导扇区和setup部分, 生成setup.elf. 然后进行strip, 生成setup.bin.
               
                最后, 利用内核工具"build", 将setup.bin和vmlinux.bin拷贝在一起, 并填上必要的信息如setup部分的大小等, grub等引导程序可以使用的bzImage诞生了.


        2.3 内核实模式代码
                实模式代码位于arch/x86/boot. 记得Linux2.6.18之前所有的代码都用汇编写成, 2.6.18之后大部分替换成了C.
                入口点在header.S文件, 即包含了无用的"引导扇区代码", 也包含了引导程序能识别的头部信息. 第一条可执行语句在偏移0x200的位置(跳过引导扇区), 执行必要的初始化操作, 然后将控制权交给C程序, 即main.c里的main()函数.
                有了main()函数, 接下来的过程就像读文档一样方便了 利用实模式的优势, 调用BIOS执行必要的初始化和参数获取, 并将结果存到结构体boot_params.
                在main()的最后调用go_to_protected_mode(). 这个函数位于pm.c, 它打开A20, 初始化协处理器(如果有), 关掉所有中断, 设置空的IDT和最基本的GDT, 接着调用protected_mode_jump跳转到保护模式的入口代码0x100000. 这个函数定义在pmjump.S. 注意跳转的时候, boot_params被放在esi寄存器.

        2.4 保护模式:自解压过程
                内核保护模式的入口在arch/x86/boot/compressed/head_32.S (32位架构). 我们利用bochs的调试功能, 跟着走一遍:

       
  1. pb 0x100000
  2. c
复制代码

                接着grub会运行, 选择我们编译的kernel, 等实模式代码运行完毕, 就会断在保护模式的入口. 反汇编看一下:

       
  1. u /16 0x100000
复制代码

                可以看到反汇编代码和head_32.S一样. (如果不一样.. 报告一起UFO目击事件吧)

                在这里, esi还是指向来自与实模式的boot_params. 接下来的任务, 就是拷贝和解压. 目的地在0x1000000(16MB). 解压部分是C语言函数.
                最后跳转到0x1000000.       


3. 内核启动

        3.1 平台相关的初始化
        我们将断点设在内核的入口点 0x1000000(16MB), 继续执行, 内核会自己解压, 并停在入口点.
        执行反汇编操作:

  1. u /16 0x1000000
复制代码
        对应的汇编文件在 arch/x86/kernel/head_32.S. 这个是真正的内核入口点. 在这里初始化页表, 并启动分页机制. 打开SMP的化, 进行必要的CPU初始化. 然后初始化IDT, 检查CPU类型, 最后跳转到C语言的i386_start_kernel(). 这个函数位于head32.c. 它保留一些内存并做标记后, 跳转到平台无关的start_kernel(), 位于init/main.c.

        <待细化, 欢迎补充>


        3.2 平台无关的初始化
        start_kernel()是一个高层函数, 与架构无关, 但指挥架构相关的代码完成剩余的初始化部分.

        <待续>

        <若有异议, 欢迎讨论一起研究>
        个人学习笔记, 如需转载, 请注明出处: 张力辉 <swordhui@hotmail.com>

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
发表于 2011-1-1 01:51:05 | 显示全部楼层
在Ubuntu 10.10中
grub-mkimage -o core.img biosdisk part_msdos ext2
应该加上 -O i386-pc
即:
grub-mkimage -o core.img biosdisk part_msdos ext2 -O i386-pc,
写得很不错,不过能不能加上制作rootfs的过程呢,让大家学习学习
回复 支持 反对

使用道具 举报

发表于 2011-9-2 00:30:45 | 显示全部楼层
to all
请问您使用的是什么版本的bochs,好像bochs 2.4.6+grub 1.99 有问题
回复 支持 反对

使用道具 举报

发表于 2011-12-24 11:19:16 | 显示全部楼层
bochs 2.4.6 出现 unaligned pointer 0x****
bochs 2.5没有问题
回复 支持 反对

使用道具 举报

发表于 2012-2-9 15:02:54 | 显示全部楼层
我使用grub 0.97,进入grub shell后,setup出错。
root 命令能够识别出0分区是ext2,怎么到setup又找不到了?难道还要安装分区1?

#grub --device-map=./device.map --config-file=./menu.lst --no-floppy
grub> root (hd0,0)
Filesystem type is ext2fs, partition type 0x83

grub> setup (hd0)
Checking if "/boot/grub/stage1" exists... yes
Checking if "/boot/grub/stage2" exists... yes
Checking if "/boot/grub/e2fs_stage1_5" exists... yes
Running "embed /boot/grub/e2fs_stage1_5 (hd0)"...  15 sectors are embedded.
succeeded
Running "install /boot/grub/stage1 (hd0) (hd0)1+15 p (hd0,0)/boot/grub/stage2 /boot/grub/menu.lst"... failed

Error 22: No such partition
回复 支持 反对

使用道具 举报

发表于 2012-2-10 12:56:57 | 显示全部楼层
试试grub-install
回复 支持 反对

使用道具 举报

发表于 2012-2-11 09:01:53 | 显示全部楼层
grub-install 也是同样的错误
处理过程应该是一致的,grub是交互式的,grub-install封装成批处理方式了吧.
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表