qemu中跑linux

来源:互联网 发布:淘宝网店怎么上架宝贝 编辑:程序博客网 时间:2024/06/05 22:45

http://blog.csdn.net/g__gle/article/details/7004956

用官方源码编译内核,在qemu中使其尽快跑至shell。以为很简单的事,結果折腾了半个下午。

表面上看来,这件事就像极端简化的LFS。Ubuntu的仓库里居然还有静态编译的busybox!辅以initramfs,用户态的初始环境很容易就构造了。

比较麻烦的是bootloader,内核要怎么到内存中来?好在qemu有个-kernel参数,后加bzImage文件就可以完成bootloader所做的事。看起来只需要两步就OK了:


1.make defconfig && make bzImage

2.qemu -kernel arch/x86/boot/bzImage


結果是Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(8,5)


暂不想操心硬盘的事,只想在内存里跑。新建文件夹,把静态编译好的busybox复制至其中并命名为init。在menuconfig里将initramfs路径指向文件夹,再来。另外不想让qemu用图形界面。


qemu -kernel arch/x86/boot/bzImage -nographic


結果,显示qemu的几行启动信息之后,神马反应也没有,杀也杀不掉。想了一下,默认的console应该是VGA显卡,在cmdline里指定为串口试一下。


qemu -kernel arch/x86/boot/bzImage -append “console=ttyS0”  -nographic


好了,有输出了,但是最后停在[    1.577268] Switching to clocksource tsc,怎么按键盘都没反应。往上看两行注意到了[    1.253342] Warning: unable to open an initial console。看了一下本机上的console文件,字符设备,5,1.在initramfs文件夹里新建dev目录,再在其中sudo mknod console c 5 1,重编译,再试。终于出现init找不到配置文件之类的提示。


暂不想管inittab什么的,直接让shell做1号进程。在initramfs文件夹里新建bin目录,将init移动为bin下的sh。编译,cmdline中指定init,再试。


qemu -kernel arch/x86/boot/bzImage -append "console=ttyS0 init=/bin/sh" -nographic


結果居然是[    1.402899] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(8,5)!


为什么有initramfs还会去尝试挂载根文件系统?研究了一下代码,发现是自己以前看代码不仔细。再试。


qemu -kernel arch/x86/boot/bzImage -append "console=ttyS0 rdinit=/bin/sh" -nographic


终于启动成功了,shell也可以跑了。里面什么命令也没有,预先在initramfs文件夹里建一些指向busybox的软链接就好了。

=========================================================

关于启动的细节。以前的认识有些偏差。且不说initrd,内核镜像里总有一个内嵌initramfs的cpio包,好像还压缩了。在初始化的时候,先初始rootfs,在内存中构建了最初的文件系统。然后不管三七二十一,将镜像中的cpio包释放出来。


怎么会报Unable to mount root fs的错呢?进行到kernel_init时,有这样的代码:

[cpp] view plaincopyprint?
  1. if (!ramdisk_execute_command)  
  2.         ramdisk_execute_command = "/init";  
  3.   
  4. if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {  
  5.         ramdisk_execute_command = NULL;  
  6.         prepare_namespace();  
  7. }  

也就是说,如果在初始的文件系统中找不到ramdisk_execute_command(默认是init,可以在cmdline中覆盖),则进入prepare_namespce。这个里面就会有尝试mount root fs的动作。如果只想在内存中跑,一定要使sys_access成功。而覆盖ramdisk_execute_command的命令行参数不是init=而是rdinit=!


在其后的init_pos中。如果ramdisk_execute_command非空,则执行该程序,内核的工作到此为此,带起系统的事是initramfs里的init负责。如果initramfs里没有能带起系统的init,内核则尝试挂载真正的物理根文件系统(prepare_namespace),然后再执行真正根文件系统的init程序。这个程序的路径由init=所指定,如果不指定,则做如下尝试:

[cpp] view plaincopyprint?
  1. run_init_process("/sbin/init");  
  2. run_init_process("/etc/init");  
  3. run_init_process("/bin/init");  
  4. run_init_process("/bin/sh");  


总结:

    1.如果initramfs里有init(rdinit指定或者是/init),1号进程会执行它,系统启动则完全交给它了。

 2.initramfs里的init执行失败(不存在或者其它原因),内核会默不作声地进入老式启动流程。

 3.从上一步滑下来了,则尝试挂载根文件系统所在设备(老式的启动流程),设备号默认为编译内核的主机上挂载在/下设备的设备号,在cmdline中可以用root=覆盖之。VFS: Unable to mount root fs就是这时产生的。挂载完根文件系统中之后,执行execute_command,即cmdline中init=所指定的。如果没有指定,则尝试默认四种可能。如果killing init导致panic并且提示你加init=的话,那应该就是挂载了真正的根文件系统里没有init=所指定的程序且四个默认的init程序皆执行失败。

 4.如果没有指定initramfs source,内核会生成一个最小的initramfs cpio包,这个包不是空的!它有一个dev目录,下面有一个console结点,它还有一个root目录。如果指定initramfs source,则自己的initramfs目录里最好有dev目录及其下在的console结点。另外,如果内核去挂载根文件系统所在设备,这个设备会被挂载在/root下!假设自己提供initramfs,内核里没能执行其中的init,这个initramfs里又没有/root目录,在内核尝试挂载根文件系统所在设备时会失败,因为挂载点不存在(报错却报却是VFS: Cannot open root device;当然也有可能真没那个设备)。

    5.由于代码流程,如果initramfs里有init,cmdline里的root=和init=都不起作用.


原创粉丝点击