接触Linux很长时间了,一直没有什么长进,每次遇到问题解决完了就扔到一边,导致现在很多事情都无法手到即来。定制自己的U盘Linux,从去年就已经完工了,但是一直没有心思整理,过完年好不容易有点时间,抓紧整理一遍,以备不时之需。
总的来说,定制Linux系统的步骤还是比较简单的,因为还是在x86体系下,不用交叉编译,所以可以划分为五个部分:准备U盘,安装grub,编译busybox,编译内核,制作文件系统。其中grub我觉得是比较难以搞定的一件事情,很早前用的0.97版本,后来升级到2.0版本,都是稀里糊涂的就做成了,这次重新整理,详细记录一下方法步骤,也算在Linux的海洋里刨过水了。由于时间过去太久,此次全部下载最新版本的工具重新开始。
1.准备U盘
这一步其实算不上是制作过程中的核心步骤,但是既然用U盘承载Linux系统,还是应该给其留出一席之地的。准备工作就是给U盘分区并创建文件系统,用到fdisk和mkfs两个工具,比较简单。分区大小自己设定,因为一开始系统都不会太大所以Linux分区有500M足够用了,再大也不嫌弃,文件系统暂时用ext2,如果为了让U盘还能在windows上使唤,可以再划出一个Fat32分区,例如下图这样:
2.安装grub
grub官网地址:https://www.gnu.org/software/grub/ (可恨在百度和bing搜索grub,第一条都不是此链接,最终还是google的作风正派),目前最新版本是grub2,官方ftp提供的下载文件创建时期是2012.06.28,看来一直也没有改动过。下载包后依照正常模式configure,make,make install,这一步也不是必须的,因为像Ubuntu系统上就已经安装了grub,直接用就行了。这次既然重新来,就编译试试,因为遇到了一些错误,所以也记录在案:
configure的时候可能会提示缺少包,好在现在用的是Ubuntu,所以缺什么装什么,不成问题。
make的时候提示两个错误:
./stdio.h:456:1: error: 'gets' undeclared here (not in a function)
_GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");
解决方法是在stdio.h文件中,添加下面两句(蓝色部分)
#ifdef gets
#undef gets
_GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");
#endif
另外一个错误:
grub_script.yy.c:2351:13: error: 'yy_fatal_error' defined but not used
解决方法是重新配置,添加蓝色部分选项: ./configure --disable-werror
(参考https://lists.gnu.org/archive/html/grub-devel/2012-08/msg00006.html)
原来在ubuntu12.04和RedHat6中编译一切都很正常,可是到了ubuntu15.10就出现问题,根本原因还是gcc版本更新了的缘故,好在前人也都已经总结出了经验。make完成之后会在当前目录下产生很多可执行文件,执行make install安装即可。
开始正式给U盘安装grub:
挂载U盘:
#mount /dev/sdb2 /media
安装:
#grub-install --root-directory=/media /dev/sdb
Installing for i386-pc platform.
Installation finished. No error reported.
查看安装后目录:
#ls /media/boot/grub/
fonts grubenv i386-pc locale
对比ubuntu系统的grub目录,基本内容一致:
#ls /boot/grub/
fonts gfxblacklist.txt grub.cfg grubenv i386-pc locale unicode.pf2
至此grub就算安装成功了,为了验证,做一个U盘启动的虚拟机(关于如何U盘启动,请参考virtualbox使用U盘启动),虚拟机启动后只要能看到这个界面,就说明一切正常:
接着配置grub.cfg文件,让系统可以自动引导:
1 2 3 4 5 6 7 8 9 10 11 12 |
set default=0 insmod gzio insmod jpeg insmod part_msdos insmod ext2 set timeout=10 set root='hd0,2' linux (hd0,2)/boot/vmlinuz rw initrd (hd0,2)/boot/initrd.img boot |
配置很简单,一切从简,只要能工作,剩下的事就好办多了。此处唯一要注意的地方就是(hd0,2),因为在分区U盘的时候,默认linux分区在第二位,所以此处应该是(hd0,2)而不是(hd0,1),注意了! 而vmlinux是下面要编译出来的内核文件,initrd.img则是制作好的ramdisk文件。
3.busybox1.24.1
单纯编译busybox算是比较简单的一部分,tar -jxvf 解压tar.bz2包,然后执行make menuconfig,选择自己需要的内容,接着make,make install,一般都不会有太大的问题,要是没有错误会生成到当前./_install目录下,在制作文件系统时,将生成的所有文件夹通通拷贝过去即可。busybox选项中都是要选择的命令,很明了,option中只需要注意将busybox编译成static的模式即可,最后生成的内容如下:
busybox默认带有make defconfig选项,官方说可以支持几乎所有的功能,懒人专用。如果这种方式编译提示错误:
klogd should not be used together with syslog to kernel printk buffer
就将.config文件中CONFIG_KLOGD=y 一行注释掉,再重新make就可以通过,中途会提示是否使用KLOG,选择"n"
4.编译内核linux-4.4.1
内核编译也算比较长见识的一项工作,以前感觉很神秘,操作其实很简单的,因为内核的价值在于其内部实现和各种功能的选择和精简,而不是编译本身,当下只是舍本逐末而已。依然是make menuconfig,然后选择模块。最要命的是对U盘的支持,常见的错误就是在启动是提示:" VFS: unable to mount root fs on unkown-block(1,0)":
出现这种错误这时最好能将启动过程录屏,然后慢慢播放查看之前的日志,会有详细的错误描述。官方也认为导致这个错误出现的原因是多种多样的,特别应该关注的就是file system,block device这两个地方。经验是下面的这几个选项必须得设置:
General setup -->Initial RAM filesystem and RAM disk (initramfs/initrd) support
-->Support initial ramdisks compressed using gzip
Device Drivers --> Block devices
--> RAM block device support -->
(4) Default number of RAM disks
(655360)Default RAM disk size (kbytes)
File system --> Second extended fs support
-->The Extend 3 (ext3) filesystem
总结一下就是block device一定要选中RAM block device support,然后默认的RAM disk size的大小一定要比自己的ramdisk大一点,文件系统只要包含涉及到的文件系统即可,其他SCSI设备的支持选项都是默认选中的。在编译内核时,可以用make -j 4 的方式,这样能加快一些速度,关于是否用ccache,根据个人情况考虑,如果每次只改动很小部分或许有用。
最新的内核版本在make之后,会自动产生bzImage文件,在./arch/x86/boot目录下,将其拷贝到U盘:
#cp ./arc/x86/boot/bzImage /media/boot/vmlinuz
5.制作文件系统
目前暂时用RAMDisk,就是将所有的文件都放到虚拟出来的RAM磁盘方式来实现,这样做的好处就是简单。首先创建一个目录fsystem,用于存放所有要打包到ramdisk里面的内容,先将busybox产生的内容拷贝过来,然后再建立一些必须的系统目录:
#mkdir dev etc lib home opt proc sys var tmp
接着拷贝设备文件:
通过"cp -a"命令将系统 /dev/下面的设备:
cdrom,dvd,loop0,console,random,urandom,ram0,null,zero,tty,tty0
统统拷贝到fsystem/dev下面
拷贝一些系统库:
#cp /lib/i386-linux-gnu/{libc.so.6,libm.so.6,libpthread.so.0,librt.so.1,ld-linux.so.2} ./
创建块文件:
#dd if=/dev/zero of=ramdisk.img2 bs=4000 count=20480
格式化分区:
#mkfs.ext4 -F -m 0 -b 1024 ramdisk.img
挂载块文件:
#sudo mount -t ext4 -o loop ramdisk.img /media/
将已经做好的文件文件系统全部拷贝到ramdisk中:
#cp -r fsystem/* /media
卸载文件系统:
#umount /media
用gzip压缩ramdisk:
#gzip -9 ramdisk.img
到此ramdisk就制作好了,拷贝到U盘上:
#cp ramdisk.img.gz /boot/initrd.img
一切准备就绪,启动虚拟机,如果正常进入命令提示符,就算成功了一大半了:
这次重新编译内核,断断续续的花了一周时间,"mount root fs"的错误比较恼人,重复编译/测试了十几次,才最终找到ramdisk大小配置的问题。另外etc目前是空置的(没错,没有任何配置文件内核也可以可以启动的:-(),但是一些依赖相关配置文件的功能可能就没法正常工作,例如fdisk和df等,这部分留在后面继续研究吧。
参考:
http://www.ibm.com/developerworks/cn/linux/l-initrd.html