异想天开

What's the true meaning of light, Could you tell me why

捣鼓linux

日期:2014-03-17 20:35:54
  
最后更新日期:2016-11-04 20:32:46
目录:
1.实验环境以及解释这样折腾的目的
2.利用busybox制作根文件系统
3.编译linux内核
4.bochs以及将grub安装到虚拟硬盘上
5.实践操作脚本
关键字:bochs调试linux kernel,根文件系统

一.实验环境以及解释这样折腾的目的
本文的所有操作都是在虚拟机上面进行的,虚拟机利用两个虚拟磁盘镜像,其中一个是系统盘-操作系统安装在这个盘里面,另外一个是数据盘-进行操作时数据存放的地方。这样做有很多好处:1.大多数情况下,非常有可能有误操作,为了防止系统启动不了,数据丢失了,我会进行对这两个虚拟硬盘进行备份,这样,即使操作错误,我也只需要思考下一下前一个步骤错误的原因,然后改进它,接着操作即可。PS:没错,这就是软件迭代的思想,任何复杂的问题都可以由此化为一系列简单小问题。2.用两个虚拟硬盘,有时可以节约备份时间,因为有时候只改动了一个盘,就没有必要备份两个盘了。这样捣鼓的目的只有一个:研究其源代码。
创建数据盘步骤: 1.用virtualbox新建一块虚拟硬盘,选择格式时,最好选择vdi,因为在virtualbox的命令行工具里面可以对这种格式进行压缩;把这块盘挂载在你的系统,然后该盘被你的系统识别为/dev/sdb,也可能是/dev/sdc。
2.建立一个分区即可;
sudo fdisk /dev/sdb
3.格式化为ext4文件系统.
sudo mkfs.ext4 /dev/sdb1
4.自动挂载,在/etc/fstab添加一行
"/dev/sdb1 /home/mooccf/data ext4 auto defaults 0 0 " 这里可以用UUID来挂载,这样就与硬盘连接计算机顺序无关了,
UUID="17969436-2f15-4b1d-b7ff-17916b2cbe54" /home/mooccf/data ext4 auto defaults 0 0
就可以实现自动挂载

二.利用busybox制作根文件系统
步骤:
1.去官网下载一个busybox的源代码tar包。有时候个别版本在你的系统不能用,先确定是不是网上已经解决好的问题。这些问题是正常的,比如我在编译busybox 1.17时,提示找不到/usr/include/linux/ext2_fs.h,但当时我是新编译好的3.13版本内核,编译完之后,用make headers_install安装了头文件。确定不是系统的问题之后,我换了另外一个版本的busybox-1.22,可以顺利编译。
2.选择编译busybox为静态
[code lang="cpp"]
make menuconfig

Busybox Settings --->
Build Options --->
[ * ] Build BusyBox as a static binary (no shared libs)
General Configuration --->
[ * ] Don't use /usr
Miscellaneous Utilities --->
[ ] flashcp
[ ] flash_lock
[ ] flash_unlock
[ ] flash_eraseall

注:[ ] 表示不选择
[/code]
保存配置,make && make install就会在改目录下产生一个_install目录。这里可以查看文章后面附录的脚本来建立一个在虚拟硬盘上面的根文件系统。建立好根文件就可以用qemu来模拟启动了。以下为详细步骤:
1.创建虚拟盘,并格式化它。
[code lang="cpp"]
mooccf@DigitalSystemsVM:~/data/work$ sudo dd if=/dev/zero of=hd0.img bs=1024 count=$((63*16*500))
mooccf@DigitalSystemsVM:~/data/work$ ll hd0.img
-rw-r--r-- 1 root root 516096000 Apr 11 08:39 hd0.img
mooccf@DigitalSystemsVM:~/data/work$ sudo losetup /dev/loop6 hd0.img
mooccf@DigitalSystemsVM:~/data/work$ sudo fdisk /dev/loop6
#n添加一个分区,a将该分区标记为boot ,w保存分区表
mooccf@DigitalSystemsVM:~/data/work$ sudo fdisk -l /dev/loop6
sudo: /var/lib/sudo owned by uid 1000, should be uid 0
[sudo] password for mooccf:
Disk /dev/loop6: 516 MB, 516096000 bytes
190 heads, 63 sectors/track, 84 cylinders, total 1008000 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x881fc94b
Device Boot Start End Blocks Id System
/dev/loop6p1 * 2048 1007999 502976 83 Linux
mooccf@DigitalSystemsVM:~/data/work$ sudo losetup -o $((2048*512)) /dev/loop7 /dev/loop6
mooccf@DigitalSystemsVM:~/data/work$ sudo mkfs.ext3 /dev/loop7
mooccf@DigitalSystemsVM:~/data/work$ sudo blkid /dev/loop7
sudo: /var/lib/sudo owned by uid 1000, should be uid 0
[sudo] password for mooccf:
/dev/loop7: UUID="3fe27443-c06e-41bb-95bf-75e510daebae" TYPE="ext3" SEC_TYPE="ext2"
[/code]
2.复制拷贝busybox某些文件到hd0.img
[code lang="cpp"]
#挂载hd0.img的分区一,其实也就一个分区
mooccf@DigitalSystemsVM:~/data/work$ sudo mount -t ext3 /dev/loop7 mnt/
#将busybox编译后生成的_install里面bin拷贝到mnt
mooccf@DigitalSystemsVM:~/data/work$ sudo cp -R ../busybox-1.22.1/_install/ mnt/
#在分区上建立类似linux根目录结构
mooccf@DigitalSystemsVM:~/data/work$ sudo mkdir mnt/{sbin,dev,etc,proc,boot,lib,sys,tmp,mnt}
#拷贝busybox的etc配置文件
mooccf@DigitalSystemsVM:~/data/work$ sudo cp -R ../busybox-1.22.1/examples/bootfloppy/etc/ mnt/etc/
#编辑mnt/etc/init.d/rcS文件
#!/bin/sh
/bin/mount -a
mkdir /dev/pts
/bin/mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev -s
#编辑mnt/etc/fstab
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
[/code]

三.编译linux内核
PS:在虚拟机里编译内核,注意要用make O=outputdir来制定输出目录,有时,你没有注意到你的源代码目录空间不足,就开始编译,直到编译失败才发现问题,而那时,你有可能已经在编译了好几个小时。一般初次编译时,配置不要乱改,最好是直接用boot目录下的配置文件,这样不容易出错。轻车熟路之后,你再慢慢尝试。
介绍linux的makefile的文章:linux的makefile分析 将编译好的linux内核拷贝到hd0.img中分区的boot目录,这里我们不需要initrd了。
[code lang="cpp"]
mooccf@DigitalSystemsVM:~/data/work$ sudo cp ../obj/arch/x86/boot/bzImage mnt/boot/vmlinuz
[/code]

四.bochs以及将grub安装到虚拟硬盘上
将grub安装在虚拟硬盘,并且用bochs来启动,这里是我尝试时间最久的一项。最好,值得说的是:
1.可能网上查找的资料,在你的系统并没有相同的命令,所以网上的解决方案在你的系统需要本地化才能成功。安装grub时,我先是用grub-install在虚拟硬盘上来安装,显示是no error reports。但是启动时就进去rescue救急模式,用ls也看不到分区。网友说,先建立一个grub-mkimage来创建一个包含基本的mod的core.img,然后用core.img来grub-setup上去。我的系统没有发现grub-setup,但有个grub-bios-setup,猜想功能应该差不多。结果还是进入rescue模式,说找不到磁盘,几次失败后,我终于发现了提示的那个磁盘的uuid,是我宿主系统上的uuid,也就是我将虚拟硬盘挂到/dev/loop0设备上在/dev/disk/by-uuid里面可以看到。我意识到是设备映射出了问题。后面我在网上找这方面的思路。指定一个device.map就解决了。比如你安装grub到u盘上,那么这个时候你的系统上对于该u盘是/dev/sdb,但是你用u盘引导启动时,u盘应该是第一块硬盘,也就是hd0。详细方法见附录。 1.制作嵌入菜单配置文件,我的虚拟硬盘分区挂载到/dev/loop1。
[code lang="cpp"]
mooccf@DigitalSystemsVM:~/data/work$ sudo losetup /dev/loop0 a.img
sudo: /var/lib/sudo owned by uid 1000, should be uid 0
[sudo] password for mooccf:
mooccf@DigitalSystemsVM:~/data/work$ sudo losetup -o $((2048*512)) /dev/loop1 /dev/loop0
sudo: /var/lib/sudo owned by uid 1000, should be uid 0
[sudo] password for mooccf:
mooccf@DigitalSystemsVM:~/data/work$ sudo mount -t ext3 /dev/loop1 mnt/
sudo: /var/lib/sudo owned by uid 1000, should be uid 0
[sudo] password for mooccf:
mooccf@DigitalSystemsVM:~/data/work$ sudo blkid /dev/loop1
sudo: /var/lib/sudo owned by uid 1000, should be uid 0
[sudo] password for mooccf:
/dev/loop1: UUID="ca13dfbe-2335-4dc4-baa6-b294e2ee1376" TYPE="ext3"

mooccf@DigitalSystemsVM:~/data/work$ cat boot/emb.cfg
search --fs-uuid --no-floppy --set=root ca13dfbe-2335-4dc4-baa6-b294e2ee1376
prefix=($root)/boot/grub
configfile /boot/grub/grub.cfg

mooccf@DigitalSystemsVM:~/data/work$ cat mnt/boot/grub/grub.cfg
set default="0"
set timeout=5
set root=(hd0,msdos1)
menuentry "Just Fuck Code" {
insmod part_msdos
insmod ext2
set root=(hd0,msdos1)
linux /boot/vmlinuz root=/dev/sda1 apic=debug noapic
boot
}
[/code]
搜索指定uuid的硬盘为root目录

2.制作core.img,core.img主要为第一阶段的引导部分
[code lang="cpp"]
sudo grub-mkimage -c $pwd/boot/emb.cfg -p /boot/grub \
-O i386-pc -d /usr/lib/grub/i386-pc \
-o core.img \
loadenv biosdisk part_msdos part_gpt fat ntfs \
ext2 ntfscomp iso9660 loopback search linux boot minicmd cat cpuid chain \
halt help ls reboot echo test configfile normal sleep memdisk tar font \
gfxterm gettext true vbe vga video_bochs video_cirrus multiboot multiboot2\
usb usbtest usb_keyboard
[/code]

3.安装grub到虚拟盘上
[code lang="cpp"]
sudo grub-bios-setup --boot-image=boot.img --directory=$pwd/boot --core-imag
e=core.img \
/dev/loop0
[/code]

4.如果用gdb stub调试的方法遇到page fault错误,那么修改bochs源代码。大概原因是bochs发会发送signal 0消息,而gdb收到该消息会暂停。将gdbstub.cc中这段代码
[code lang="cpp"]
if (last_stop_reason == GDBSTUB_EXECUTION_BREAKPOINT ||
last_stop_reason == GDBSTUB_TRACE)
{
write_signal(&buf[1], SIGTRAP);
}
else
{
write_signal(&buf[1], 0);
}
[/code]
改为如下:
[code lang="cpp"]
if (last_stop_reason == GDBSTUB_EXECUTION_BREAKPOINT ||
last_stop_reason == GDBSTUB_TRACE)
{
write_signal(&buf[1], SIGTRAP);
}
else if(last_stop_reason==GDBSTUB_STOP_NO_REASON)
{
write_signal(&buf[1],SIGSEGV);
}
else
{
write_signal(&buf[1], 0);
}
[/code]

5.最后用bochs或qemu来启动。
[code lang="cpp"]
mooccf@DigitalSystemsVM:~/data/work$ cat bochsrc1
megs: 64
romimage: file=$BXSHARE/BIOS-bochs-latest
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
# hard disk
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
#255 heads, 63 sectors/track, 45
ata0-master: type=disk, path="/home/mooccf/data/work/a.img", mode=flat, cylinders=1000, heads=16, s
pt=63
# choose the boot disk.
boot:c
log: bochsout.txt
# disable the mouse, since DLX is text only
mouse: enabled=0
# set up IPS value and clock sync
cpu: ips=15000000
clock: sync=both
gdbstub: enabled=1,port=4321
#gdbstub这行配置表示bochs与gdb通信,通过gdb来调试bochs里面的运行可执行程序
[/code]
然后再执行下面几个命令:
[code lang="cpp"]
mooccf@DigitalSystemsVM:~/data/work$objcopy --only-keep-debug vmlinux vmlinux.dbg
mooccf@DigitalSystemsVM:~/data/work$ ll -hl vmlinux.dbg
-rwxr-xr-x 1 mooccf mooccf 200M Apr 5 15:09 vmlinux.dbg*
#抽取出来符号信息,占了很大一部分,所以也没有必要抽取直接用vmlinux即可
mooccf@DigitalSystemsVM:~/data/work$sudo sudo /usr/local/bochs_2_6_2/bin/bochs -qf bochsrc
#然后在另外一个终端里
mooccf@DigitalSystemsVM:~/data/work$gdb
#进入后执行下面命令
file vmlinux.dbg
target remote localhost:4321
set disassembly-flavor intel
set history expansion
set history save
set step-mode on
handle SIGSEGV nostop noprint ignore
layout split
[/code]
如果看到这张图后,那么表示成功了:
bochs和gdb调试 五.附录 1.实践中我将所有操作写为了bash脚本
[code lang="cpp"]
#!/bin/bash

BUSYBOX=/home/mooccf/data/busybox-1.22.1
pwd=`pwd`
MNT=/home/mooccf/data/work/mnt
IMAGE=/home/mooccf/data/work/a.img
LOG=$IMAGE.txt
WORK=/home/mooccf/data/work

sudo umount $MNT
sudo losetup -d /dev/loop7
sudo losetup -d /dev/loop6
sudo rm -f $IAMGE

echo $MNT
echo $IMAGE
echo $pwd
#create img
cmd_choose=2
#创建磁盘,也用了两种方法,后来发现没必要都是产生一个全是0的虚拟硬盘,里面没有什么参数
if [ "$cmd_choose" -eq 1 ]
then
/usr/local/bochs_2_6_2/bin/bximage >$LOG <<EOF
hd
flat
360M
$IMAGE
yes
EOF
echo 'choose bximage'
else
echo 'dd'
sudo dd if=/dev/zero of=$IMAGE bs=1024 count=$((63*16*500))
fi


#fdisk img,only one partition
sudo losetup /dev/loop6 $IMAGE
cmd_choose=1
#这里fdisk,测试了两种方法,可能在你的系统某一种方法能行
if [ "$cmd_choose" -eq 1 ]
then
sudo fdisk /dev/loop6 >>$LOG <<EOF
o
n
p
1

+300M
a
1
n
p
2


w
EOF
else
sudo cfdisk -s63 -h16 /dev/loop6
fi

echo 'please check the a.img'
read just_wait
sudo cfdisk /dev/loop6

sudo losetup -o $((2048*512)) /dev/loop7 /dev/loop6
sudo mkfs.ext3 /dev/loop7
sudo mount -t ext3 /dev/loop7 $MNT/
cd $MNT/
sudo chown -R mooccf ./
#copy file
sudo cp -R $BUSYBOX/_install/ $MNT/
sudo mkdir $MNT/sbin
sudo mkdir $MNT/etc
sudo mkdir $MNT/proc
sudo mkdir $MNT/lib
sudo mkdir $MNT/sys
sudo mkdir $MNT/dev
sudo mkdir $MNT/mnt
sudo mkdir $MNT/tmp

sudo cp -R $BUSYBOX/examples/bootfloppy/etc/ $MNT/etc/
printf "#!/bin/sh\n/bin/mount -a\nmkdir /dev/pts\n/bin/mount -t devpts devpts /dev/pts\necho /sb
in/mdev > /proc/sys/kernel/hotplug\n/sbin/mdev -s"> $MNT/etc/init.d/rcS

printf "proc /proc proc defaults 0 0\ntmpfs /tmp tmpfs defaults
0 0\nsysfs /sys sysfs defaults 0 0\ntmpfs /dev tmpfs defau
lts 0 0" > $MNT/etc/fstab

cd $MNT/dev/
sudo mknod null c 1 3
sudo mknod console c 5 1

cd $MNT/bin/
sudo chmod u+s ./busybox
sudo ln -sf /bin/busybox ls
sudo ln -sf /bin/busybox sh
sudo ln -sf /bin/busybox mount
sudo ln -sf /bin/busybox umount
sudo ln -sf /bin/busybox mkdir
cd $MNT/sbin/
sudo ln -sf /bin/busybox init
sudo ln -sf /bin/busybox mdev


#return back
cd $MNT/
sudo chown -R root ./

cd $pwd
echo $pwd
sudo mkdir $MNT/boot
cd $pwd/boot
str=`sudo blkid /dev/loop7 `
echo $str
uuid=`echo ${str:18:36}`
echo $uuid

echo 'please check the a.img uuid'
read just_wait

#set e
printf "search --fs-uuid --no-floppy --set=root $uuid\nprefix=(\$root)/boot
/grub
\nconfigfile /boot/grub/grub.cfg
" > $pwd/boot/emb.cfg
#sudo grub-mkimage --prefix="(hd0,msdos1)/boot/grub" \
sudo grub-mkimage -c $pwd/boot/emb.cfg -p /boot/grub \
-O i386-pc -d /usr/lib/grub/i386-pc \
-o core.img \
loadenv biosdisk part_msdos part_gpt fat ntfs \
ext2 ntfscomp iso9660 loopback search linux boot minicmd cat cpuid chain \
halt help ls reboot echo test configfile normal sleep memdisk tar font \
gfxterm gettext true vbe vga video_bochs video_cirrus multiboot multiboot2\
usb usbtest usb_keyboard

#biosdisk part_msdos ext2


echo "(hd0) /dev/loop6" >$pwd/boot/device.map

sudo grub-bios-setup --boot-image=boot.img --directory=$pwd/boot --core-imag
e=core.img \
/dev/loop6

sudo cp $WORK/boot/vmlinuz $MNT/boot/
#sudo cp $WORK/boot/initrd.img $MNT/boot/
sudo cp $WORK/boot/System.map-3.13.6 $MNT/boot/
sudo cp $WORK/boot/config-3.13.6 $MNT/boot/

sudo mkdir $MNT/boot/grub
sudo cp -r /usr/lib/grub/i386-pc/ $MNT/boot/grub
sudo cp $WORK/boot/grub.cfg $MNT/boot/grub/

#echo "just check"
#read just_wait
cd $pwd
sudo umount $MNT
sudo losetup -d /dev/loop7
sudo losetup -d /dev/loop6
echo 'success'
#test
unset e
#sudo qemu -kernel /home/mooccf/data/work/bzImage -append "root=/dev/sda1 "
-boot c -hda $IMAGE -k -en-us
#sudo qemu -kernel /home/mooccf/data/work/bzImage -append "root=/dev/sda kgd
boc=ttyS0,115200 kgdbwait " -boot c -hda $IMAGE -k -en-us -serial tcp::432
1,server
sudo /usr/local/bochs_2_6_2/bin/bochs -qf bochsrc1
[/code]
仿照dlxlinux的bochsrc启动脚本,bochsrc1在里面替换:
ata0-master: type=disk, path="/home/mooccf/data/work/a.img", mode=flat, cyli nders=1000, heads=16, spt=63
可能会遇到的问题:
1.这里bochs识别虚拟硬盘时,要求虚拟硬盘的heads小于16,否则报错。但是创建这种规格,也就是spt(每磁道的扇区数)为63,heads为16时,用cfdisk -s63 -h16 /dev/loop6 这样格式化分区,就是用指定的规格,但是这样第一个分区是从63扇区开始,而fdisk是从2048扇区开始(即1M位置处),为什么第一个分区不是紧接着mbr呢?因为mbr读写扇区的能力不强,一般都是读写post mbr region(位于mbr后面的扇区区域),用cdisk来格式化时,就要求core.img小于31.5k。但是我grub-mkimage时候,仅仅包含ext2,biosdisk,part_msdos这三个模块时候,后面还是进去救急模式,并且insmod linux.mod时,总提示在非分区的位置读写。最后,我的系统可行方案是:用fdisk来分区,post mbr region就有1m多。grub-mkimage静态编译多包含几个模块。并且只用一个分区。因为对于两个分区的读写识别貌似不行。猜想可能是由于虚拟硬盘的geomtry参数不对。所以我在脚本了用了两种方案,你可以选择尝试。但记得bochrc1配置文件的虚拟硬盘的gemotry要自己算对。
最好,我也非常希望与更多操作系统及软件爱好者交流学习:
我的qq:272651211
域名email:dream@thinks-bz.com

参考:
1.使用KGDB调试内核 on QEMU(一步一步跟我学) 2.bochs调试linux内核 3.linux grub 引导启动过程详解 4.busybox制作根文件系统 5. bochs page fault错误解决