服务器在线 - 服务器软件 - 网站地图 服务器在线,专注于服务器技术!

当前位置:主页 > 云和虚拟化 > Qemu > 正文

在qemu虚拟机中运行Linux系统

时间:2015-01-18    来源:服务器在线    投稿:泡泡    点击:

用官方源码编译内核,在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时,有这样的代码:

if (!ramdisk_execute_command)  

        ramdisk_execute_command = "/init";  

if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {  

        ramdisk_execute_command = NULL;  

        prepare_namespace();  

}  

也就是说,如果在初始的文件系统中找不到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=所指定,如果不指定,则做如下尝试:

run_init_process("/sbin/init");  

run_init_process("/etc/init");  

run_init_process("/bin/init");  

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=都不起作用.

如果您的问题仍未解决,还可以加入服务器在线技术交流QQ群:8017413寻求帮助。


相关内容
最新热点内容
推荐内容