编译linux kernel及制作initrd ( by quqi99 )

作者:张华 

   运行一个linux系统需要三项内容:

   1,kernel, 内核,一些核心的代码块,如进程管理,它要求体积很小。

   2,initrd, 进入系统所需预告加载的硬件驱动module的一个最小集。当GRUB加载kernel时,kernel会在内存中将initrd文件mount到rootfs上激活,然后kernel照着initrd中的init一步一步地加载驱动。在initrd文件中所放入的模块,必须是与操作系统同一版本kernel所编译的模块。init脚本的工作流程是:

      initrd的参考文档可见:

      1) Linux initial RAM disk (initrd) overview, http://www.ibm.com/developerworks/linux/library/l-initrd/index.html

      2)  NTTdocomo-openstack / baremetal-initrd-builder, https://github.com/NTTdocomo-openstack/baremetal-initrd-builder

      2.1, nash指令(一个文件小,内置了一些实用的指令)

      2,2 挂载主要的文件系统, 并建立设备文件所需的文件系统

         mount -t proc /proc /proc

         mount -t sysfs /sys /sys

            2.2.1,procfs映射着内存中的一个虚拟目录,用于提供硬件、进程的实时信息,会随时变动。linux为保证稳定性,不允许访问/proc下的文件,root用户也不例外。

            2.2.2, sysfs也映射着内存中的一个虚拟目录,用于硬件信息的分类, sys目录的每一个文件都只有一个字符为内容来做开关的。

            2.2.3, tmpfs也映射着内存中的一个虚拟目录,内存中的文件系统。想要速度快时,可以选择在内存建立tmpfs类型的文件系统,因为它都将建在内存中。

                   例如在内存中建立了一个tmpfs分区并挂载到/mnt/tmpfs目录 :mount -t tmpfs -o size=50M tmpfs /mnt/tmpfs/

                        [root@zhanghua proc]# df -h

                        Filesystem      Size  Used Avail Use% Mounted on

                        tmpfs            50M     0   50M   0% /mnt/tmpfs

            2.2.4, /dev/shm,它是tmpfs的一种变种,tmpfs所有的内容所放在内存中,而/dev/shm在内存与文件系统有个映射,硬盘和内存中都会有这内容。

                  速度快,能存大于内存的文件,但重启之后,内容会消失。

                  下面显示在/dev/shm中建立文件与在普通ext4文件系统建文件的速度比较:

                        [root@zhanghua proc]# time dd if=/dev/zero of=/dev/shm/test.file bs=1M count=100

            100+0 records in

            100+0 records out

            104857600 bytes (105 MB) copied, 0.0395221 s, 2.7 GB/s


            real    0m0.075s

            user    0m0.001s

            sys    0m0.041s

            [root@zhanghua proc]# time dd if=/dev/zero of=/bak/test.file bs=1M count=100

            100+0 records in

            100+0 records out

            104857600 bytes (105 MB) copied, 0.0647526 s, 1.6 GB/s


            real    0m0.090s

            user    0m0.001s

            sys    0m0.066s

            2.2.5,devfs, 所有的device都会在/dev目录建立一个对应的设备文件.

                   缺点是例如即使打印机没连在计算机上,/dev/printer文件也会存在,这样会造成在intrd阶段的设备过多,所以devfs正在被udev所取代

                   例如要用光驱时,需先在linux与光驱之间通过 mount /dev/cdrom /mnt/命令做关联

            2.2.6, udev, udev可以放在/sys目录下,不需要将所有未使用的文件建立设备文件,不再需要major number和minor number,当硬件被加载时可执行用户设置的script。

                   例如,如果/dev/cdrom是被udev建立的,而非devfs,那么当光驱被拨除时,/dev/cdrom文件就会消失。

            2.2.7,/proc/PID文件,第一个进程都会对应这要闰个文件

            2.2.8,/proc/partitions用来表示检测到的硬盘信息, major字段表示SCSI controller的slot ID,minor字段表示分区ID。

                  #[root@zhanghua proc]# cat /proc/partitions  

           major minor  #blocks  name

               8        0  488386584 sda

               8        1   82051956 sda1

            2.2.9, /sys/block,块设备

                  #[root@zhanghua proc]# cat /sys/block/

                    loop0/ loop1/  sda/   sr0/

            2.2.10, /dev/pts ( pseudo terminal slave) 副虚拟终端,其目录的文件都是由ptmx(主虚拟终端)产生的,它们是父子关系。当用ssh联机到localhost本地端之后,就会在

                   /dev/pts目录下产生一个叫做"0"的文件,当别的console也利用ssh联到这台机器时,就会出现“1“.

                  [root@zhanghua proc]# ps -ef|grep ssh

                   hua      11186  3068  0 16:01 pts/0    00:00:00 ssh hua@localhost  

                   hua      11195 11187  0 16:01 ?        00:00:00 sshd: hua@pts/3

                   如上,当一个用户以ssh登录之后,该用户就分到一个ptmx所赋予的pts资源(pts/3),所以说ssh使用的是虚拟终端,不是真正的tty接口。telnet用的则是真正的tty接口。

            2.2.11, /dev/mapper,如果使用LVM后,linux要和硬盘打交道时不再直接使用/proc/partitions下的硬盘设备,而是使用/dev/mapper下的设备再去中转。

                    # ls -l /dev/mapper/*

                      brw-rw---- 1 root disk 253, 0 jan 27 16.16  /dev/mapper/vg0-lv0

                    # cat /proc/partitions

                      major minor #blocks name

                       8     0     17528  sda

                       253   0     1111   dm-0

      3,建立最初所需使用的设备文件

         设备文件使用mknod指令建立,mknod指令用来建立字符(character)或块(block)文件。

         例:mknod /dev/tty1c41, 建立一个名为tty1的设备文件,c表示是字符文件,major=4, minor=1

      4,加载相关模块

      5,切入image所指示的硬盘中实体操作系统. (rescue mode是直接通过kernel加载initrd进入单纯的内存开机的虚拟操作系统)

         5.1, mkrootdev -t ext4 -o defaults.ro hda1, 即nash指令会将GRUB中所设备的root=xxx中的xxx路径先建立好

         5.2, mount /sysroot, 将GRUB中的root路径mount到initrd中的/sysroot下。

         5.3, switchroot这个nash指令将initrd中的/sysroot文件系统切换成/rootfs,从而切换到了硬盘中的文件系统。

   3,image, 操作系统的image文件系统,当initrd被加载后,必须为用户与文件系统牵线。

   4, init进程,在切入到用户操作系统之后,首先执行linux的init进程(pid=1), init进程再去加载/etc/rc.d/init.d/functions从而启动服务。

      关于启动级别与init进程的事儿,也可参见我的另一博文件,Linux的运行级别与解决开机故障一例 ( by quqi99 ), http://blog.csdn.net/quqi99/article/details/7436926

   5, 系统管理

      5.1, 查看CPU信息 cat /proc/cpuinfo

      5.2, 查看内存, cat /proc/meminfo 或者 free -m

      5.3, 查看usb, lsusb

      5.4, 查看PCI, lspci

      5.5, 查看开机日志, dmesg |grep -i error



   本文讲的是如何编译kernel,接下来也会研究如何制作initrd与image.

   最好使用普通用户执行下面所有操作。

1,下载内核源码

   git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git

   用git tag查看版本,将并代码切换到v3.8-rc5下, git checkout v3.8-rc5

2,配置内核(有点类似于./configure), 配置前先安装一个依赖包, sudo yum install ncurses-devel,

   make menuconfig

   说明一下,内核的配置项是三选一,yes, no, 或module。yes, no意味着直接将该特性编译或不编译到内核中,module意味着以模块形式编译,模块意味着你开机会可以通modprobe命令动态加载或卸载。

   我在这里选择的默认配置,生成的配置位于根目录下的.config文件之中。

   如果你在一个老的配置文件上更改配置的话,可以用make oldconfig命令比较与之前的配置文件的差异来验证你配置的正确性。

3,执行make命令编译,

   make

   说明一下,这条命令实际上已经包括了下面的命令:

   1)确定依赖性 make dep

   2)清理编译中间文件,make clean

   3)编译内核, make bzImage

   4)生成模块, make modules

4, 安装模块,下列命令会将模块自动安装到/lib/modules/3.8.0-rc5/目录下.

   sudo make modules_install

5, 安装内核及initrd,人工将arch/x86/boot/bzImage的内核文件拷到/boot目录即可。

   sudo cp arch/x86_64/boot/bzImage /boot/vmlinuz-3.8.0-rc5

   sudo chmod a+x vmlinuz-3.8.0-rc5

   sudo update-initramfs -u -k version 

  sudo update-grub -o /boot/grub/grub.cfg

   注意:以vmlinuz-<version>这样命名它。

  上述三步等价于make install, 但make install在自动执行update grub命令时有时候会破坏你的grub文件,特别对于进行PGP加密过的硬盘。

6,「可选」,安装符号表,只有调试时才需要用到。符号表System.map用以将内核符号和它们的起始地址对应起来,调试的时候,如果需要把内存地址翻译成容易理解的函数名和以及变量名,就会很有用。

   sudo cp System.map /boot/System.map-3.8.0-rc5

7, 建立initrd文件

   sudo mkinitrd --with=ntfs -o /boot/initrd-linux3.8.0-rc5.img 3.8.0-rc5

   以上mkinitrd命令是参照现有系统的/etc/modprobe.conf和/etc/fstab文件创建一个全新的initrd, 用--with=ntfs会从/lib/modules/3.8.0-rc5目录将ntfs模块也做到initrd里去。


   那如何要从头开始做一个initrd呢?

   1) 可以用 sudo zcat initrd-linux3.8.0-rc5.img | cpio -id 命令解压 ( initrd文件是以ext2作为文件系统中,所以可以用mount -o loop initrd.img /mnt命令加载.)

   2) 然后将模块ntfs.ko加到相应的目录,如lib/modules/3.8.0-rc5/kernel/fs/ntfs目录

   3) 将ntfs.ko模块加到init脚本

   4) 重新压缩,find | cpio -co | gzip -9 > initrd-new.img


8, 更新grub, 编译/etc/grub/grub.conf文件,添加下面内容,注意千成不要用update grub命令来更新grub哦,这可能会导致你的双系统无法用。

   menuentry 'Fedora,Linux 3.8.0' --class fedora --class gnu-linux --class gnu --class os {

        set root='(hd0,msdos9)'

        linux   /boot/vmlinuz-3.8.0-rc5 root=/dev/sda10 ro   quiet splash

        initrd /boot/initrd-linux3.8.0-rc5.img

   }


   也可以在开机时按e进入grub编辑模式,再按e一次进入kernel的设置界面:

   grub> root (hd0,msdos9)

   grub> kernel /boot/vmlinuz-3.8.0-rc5 ro root=/dev/sda9 acpi=off (注意,kernel在前的grub>光标后一定要空一行)

   grub> initrd /boot/initrd-linux3.8.0-rc5.img

   grub> boot

 

 

9, 下面讲一下用于裸机的image的制作过程,需要将虚拟机磁盘系统(raw, qcow2, vhd等)往Linux识别的ext4格式转换。

  1. create raw disk
    sudo kvm-img create -f raw /bak/kvmimages/ubuntutemplate.img 8G
  2. install kvm virtual machine
    sudo kvm -m 728 -cdrom/bak/kvmimages/ubuntu-11.04-desktop-i386.iso -drivefile=/bak/kvmimages/ubuntutemplate.img -boot d -nographic -vnc :0
    use vnc to see: vncviewer192.168.99.100:5900
  3. 启动虚机之后安装一些如SSH,cloud-init等软件
    sudo apt-get install kvm-pxe
    sudo kvm -m 728 -drivefile=/bak/kvmimages/ubuntutemplate .img -boot c -nographic -vnc :0
    sudo apt-get install openssh-servercloud-init
    sudo rm -rf/etc/udev/rules.d/70-persistent-net.rules #删它,防止添加其他网口
    sudo shutdown -h now
  4. 调整镜像, 因为openstack只接受ext4文件系统格式,故需将raw格式转化成ext4
    root@zhhua:/bak/kvmimages# sudo losetup -f  --show /bak/kvmimages/ubuntucapture.img
    root@zhhua:/bak/kvmimages# sudo losetup -a
    /dev/loop0: [0809]:5770371(/bak/kvmimages/nova.img)
    /dev/loop1: [0809]:5770373(/bak/kvmimages/ubuntucapture .img)
    root@zhhua:/bak/kvmimages# sudo fdisk-l /dev/loop1
    Disk /dev/loop1: 8589 MB, 8589934592bytes
    255 heads, 63 sectors/track, 1044cylinders, total 16777216 sectors
    Units = sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes/ 512 bytes
    Disk identifier: 0x0009d391
    Device Boot Start End Blocks Id System
    /dev/loop1p1 * 2048 15286271 7642112 83 Linux
    /dev/loop1p2 15288318 16775167 743425 5 Extended
    /dev/loop1p5 15288320 16775167 743424 82 Linux swap

显示分区是从扇区(sector)2048开始的,每个扇区是512个字节,所以是从2048 x 512 = 1048576个字节开始的。记住这个1048576,下面会用到

卸载loop后重新从1048576字节开始挂载:

sudo losetup -d /dev/loop1

sudo losetup -f -o 1048576 /bak/kvmimages/ubuntucapture.img

 

把这整个分区拷贝到一个新文件就是一个我们要的ext4文件系统镜像

sudo dd if=/dev/loop1 of=/bak/kvmimages/ubuntutemplate.img

用完loop后记得卸载,sudo losetup -d /dev/loop1

 

挂载刚创建的ext4根文件系统,修改分区加载表(/etc/fstab),注释或删除以前的,加上“LABEL=my-rootfs / ext4 defaults 0 0”一行,

最后,别忘了运行下列命令将块设备的卷标修改成我们上面设置的my-rootfs, sudo tune2fs -L my-rootfs /bak/kvmimages/ubuntutemplate.img:

sudo mount -o loop /bak/kvmimages/ubuntutemplate.img /mnt

sudo vi /mnt/etc/fstab

#UUID=98a4bc39-82a9-4d20-abf8-4aef654c1268 / ext4 errors=remount-ro 0 1

UUID=my-rootfs / ext4 defaults 0 0

# swapwas on /dev/sda5 during installation

UUID=3afdd9f7-7e1e-4172-ae32-7407b0559c51none swap sw 0 0

 

 

把内核(vmlinuz)和initrd文件拷贝出来以便后面和虚拟机镜像一起发布到OpenStack云里。使用完虚拟机镜像后记得卸载(unmount):

sudo cp /mnt/boot/vmlinuz-2.6.38-8-generic /bak/kvmimages/boot/

sudo cp /mnt/boot/initrd.img-2.6.38-8-generic /bak/kvmimages/boot/

sudo umount  /mnt

 

整个过程是,initrc或者initramfs都是一个运行在内存的小根文件系统,它有一个叫init的脚本,做完一些准备工作之后,如加载硬件的驱动,然后会切换到镜像所在的新根文件系统上,下面就是一个intramfs中init脚本的例子:

 

 

#!/bin/sh

mount -vt proc proc /proc              #很多工具都读proc的数据,故先加载

mount -vt sysfs sysfs /sys            #加载内核文件系统

insmod scsi_mod                         #要切换到镜像的新根文件系统,当然要先加载硬件用的scsi驱动模块

insmod libata

insmod ata_piix

insmod sd_mod

mdev -s  或者echo /sbin/mdev > /proc/sys/kernel/hotplug   #可以使用busybox的mdev生成动态的udev文件,也可以使用hotplug技术在加载模块的时候再加载相应的设备

mount /dev/sda /mnt                      #加载硬盘,或者直接加到根目录/中

exec switch_root /mnt /sbin/init   #通过exec会让镜像中的init进程完全替换initramfs中的init进程的空间来切换根文件分区

/bin/sh                                              #如果上述切换根文件分区失败,还可以使用initramfs的sh进程,否则会panic

所以说,这个镜像应该是linux内核直接可以认的文件系统格式,如ext4, 直虚机使用的文件格式像raw, qrow2等须像如上方式转换到ext4等格式。这样也就可以直接通过dd命令将镜像拷到/dev/sda硬盘中了(gunzip -c /mnt/sda1/hda.img.gz | dd of=/dev/hda conv=sync,noerror bs=64K)

想把整个硬盘备份到外部存储移动硬盘中的话:

1,加载移动硬盘,mount -t vfat /dev/sda1 /mnt/sda1

2,dd备份,dd if=/dev/hda conv=sync,noerror bs=64K | gzip -c  > /mnt/sda1/hda.img.gz

3, 恢复,gunzip -c /mnt/sda1/hda.img.gz | dd of=/dev/hda conv=sync,noerror bs=64K

显然, dd的缺点是备份整个硬盘分区,不管它是不是真用了。有个叫再生龙的工具(Clonezilla)就是来克服这个缺点的。下面是它的介绍:

Clonezilla是一个很好的系统克隆工具,它基于Partimage,吸取了Norton Ghost和Partition Image的优点。即不仅支持对整个系统进行克隆,而且也可以克隆单个的分区,这种灵活性可能更能适应备份者的需要.支持GNU/Linux的文件系统 ext2、ext3、reiserfs、xfs、jfs和Windows的FAT、FAT32、NTFS文件系统.Clonezilla支持使用 PXEBoot来进行Multicast克隆.这对于需要克隆大量系统的用户极为有用. Clonezilla 比起Ghost For Linux(简称G4L)有一个很显著的优势就是Clonezilla支持的文件系统格式比G4L多以外Clonezilla只备份数据,而G4L却将整个分区都备份了(即包含空数据),所以G4L将比Clonezilla占用更多的用于存放备份镜像的空间。

 

 

或者我们使用另一种方式制作OpenStack镜像(即根文件系统),例如linux-0.2.img是一个采用kvm虚机安装的raw格式的镜像,现在将它转成linux内核认识的ext4格式的根文件系统。

1, sudo mkdir -p /mnt/{raw,ext4} && sudo mount -o loop linux-0.2.img /mnt/raw

2, dd if=/dev/zero of=linux-0.2.ext4 bs=1M count=22

   mkfs.ext4 linux-0.2.ext4

   sudo mount -o loop linux-0.2.ext4 /mnt/ext4

3, sudo cp -r /mnt/raw/* /mnt/ext4/

 

直接用raw格式的做镜像也是可以的,那样就不需要转换了,glance add name="CentOS-6.4-x86_64" is_public=true container_format=ovf disk_format=raw < CentOS-6.4-x86_64.img

 

 

最后,现看一下如何挂载raw和qcow2格式的KVM硬盘镜像

raw格式

对于未分区镜像文件直接使用loop:

mount -o loop image.img /mnt/image

已分区的镜像文件:

如果已知分区的起始位置

mount -o loop,offset=32256 image.img /mnt/image

或者使用losetup + kpartx

losetup /dev/loop0 image.img

kpartx -a /dev/loop0

mount /dev/mapper/loop0p1 /mnt/image

kpartx命令的作用,是让Linux内核读取一个设备上的分区表,然后生成代表相应分区的设备。

kpartx -l imagefile 可以查看一个映像文件中的分区,使用 kpartx -a imagefile 命令后,就可以通过 /dev/mapper/loop0pX (其中X是 分区号)来访问映像。

qcow2格式

对于qcow2格式需要使用qemu-nbd这个工具

modprobe nbd max_part=63

qemu-nbd -c /dev/nbd0 image.img

mount /dev/nbd0p1 /mnt/image

如果是LVM格式的镜像:

vgscan

vgchange -ay

mount /dev/VolGroupName/LogVolName /mnt/image

最后使用结束需释放资源:

umount /mnt/image

vgchange -an VolGroupName

killall qemu-nbd

kpartx -d /dev/loop0

losetup -d /dev/loop0

 

initrd的制作和解压

initrd 的英文含义是 boot loader initialized RAM disk,就是由 boot loader 初始化的内存盘。在 linux内核启动前,boot loader 会将存储介质中的 initrd 文件加载到内存,内核启动时会在访问真正的根文件系统前先访问该内存中的 initrd 文件系统。在 boot loader 配置了 initrd 的情况下,内核启动被分成了两个阶段,第一阶段先执行 initrd 文件系统中的"init or linuxrc",完成加载驱动模块等任务,第二阶段才会执行真正的根文件系统中的 /sbin/init, Linux2.6既支持cpio-initrd,也支持image-initrd,但是cpio-initrd有着更大的优势,在使用中我们应该优先 考虑使用cpio格式的initrd.


Initrd 的主要用途


linux 发行版必须适应各种不同的硬件架构,将所有的驱动编译进内核是不现实的,initrd 技术是解决该问题的关键技术。Linux 发行版在内核中只编译了基本的硬件驱动,在安装过程中通过检测系统硬件,生成包含安装系统硬件驱动的 initrd,是一种即可行又灵活的解决方案。


1.解压 initrd

 # file initrd.`uname –r`.img (察看格式,不同的linux操作系统,这个文件格式也有不同,这个文件格式可能是cpio 也可能是ext2)

 如果是cpio格式 :

# mkdir /mnt/tmp

# cd /mnt/tmp

# cpio -idmv </tmp/initrd.`uname -r`.img

 

2 压缩initrd

2.1 mkinitrd 

#cd /lib/modules/`uname -r`

#mkinitrd /tmp/initrd.`uname -r`.img `uname -r`

#cd /tmp

#mv initrd.`uname –r`.img initrd.`uname –r`.img.gz

(initrd使用gzip压缩,如果不改名字的话,后面无法解压缩) #gunzip initrd.`uname -r`.img.gz

2.2 cpio

#假设当前目录位于准备好的initrd文件系统的根目录下

第一种方式,老的-c选项,用的是ascii码备份方式. 

# find . | cpio -c -o > ../initrd.img

# gzip ../initrd.img

 

第二种方式,新的.已经测试可用的方式.

#cd /root/busybox-1.15.3/rootfs9260

#find . | cpio -H newc -o > ../initrd_cpio.img

#cd ../

#cp initrd_cpio.img initrd_cpio_bk.img -f

#gzip initrd_cpio.img -f

#/home/u-boot-1.1.5/tools/mkimage -A arm -T ramdisk -C none -O linux -a 0x600000 -e 0x600000 -d initrd_cpio.img.gz /home/ramdisk.uboot

#ls /home/ramdisk.uboot -al

上面cpio命令的 -H 选项指定打包文件的具体格式,要生成initramfs,只能用newc 格式,如果使用其他格式,

内核会打出这样的出错信息:Unpacking initramfs...<0> kernel panic - not syncing: no cpio magic

或者出现:Unpacking initramfs...<0>Kernel panic - not syncing: bad gzip magic numbers。 如果采用了-c旧的ascii码备份方式,则内核会按照ramdisk的方式加载根文件系统。

如果你在编译内核时选上了RAM block device support (在device drivers -> block devices里),

也可以启动成功,但这就失去了cpio-initrd的意义了!。

再就是注意cpio-initrd的初始进程是 “/init”,ramdisk方式初始进程是"/linuxrc"。

另外如果采用cramfs格式的根文件系统,也需在编译内核时选上RAM block device support。

2.3  gen_init_cpio

获取 gen_init_cpio,工具 ,gen_init_cpio是编译内核时得到的,

在内核源代码的 usr 目录下,我们可以通过 以下步骤获取它,进入内核源代码 执行 :

# make menuconfig

# make usr/

这样即编译好gen_init_cpio,

gen_initramfs_list.sh 在内核源代码的 script 目录下,

将这两个 文件 copy 到 /tmp 目录下,/tmp/initrd 为 解压好的 initrd 目录,执行以下命令 制作initrd :


#制作initrd : 

# gen_initramfs_list.sh initrd/ > filelist

# gen_init_cpio filelist >initrd.img

# gzip initrd.img

# mv initrd.img initrd-'uname –r’.img

只有用这个方式压缩的initrd ,在Linux系统重启的时候才能 一正确的文件格式 boot 起来,也可以用

这种方式修改安装光盘的initrd文件 然后 进行系统安装。


3. 如何在 initrd 中添加新的驱动,以 ahci.ko 为例


3.1 gen_init_cpio 

# cp initrd-‘uname –r‘.img /tmp/initrd;cd /tmp/initrd

# cpio –ivdum < initrd-‘uname –r’.img;

# mv initrd-‘uname –r’.img ../

# cd /tmp/initrd

# vim init加上一行 insmod /lib/ahci.ko

# cp ahci.ko lib/

# cd /tmp

# gen_initramfs_list.sh initrd/ > filelist

# gen_init_cpio filelist >initrd.img

# gzip initrd.img

# mv initrd.img initrd-‘uname –r’.img

至此,新的initrd文件initrd-‘uname –r’.img中就包含了ahci的驱动程序了 ,这种方式是最简单有效的。


 3.2 mkinitrd

(1) Add “alias scsi_hostadapter ahci” at /etc/modprobe.conf

(2) copy ahci.ko to “/lib/module/$(kernel-version)”/kernel/drivers/scsi”

(3) mkinitrd initrd.img ‘uname -r’

至此,新的initrd文件initrd-‘uname –r’.img中就包含了ahci的驱动程序了 .



#释放cpio格式的initrd:       

mv initrd.img imitrd.img.gz

gunzip initrd.img.gz

cpio -i --make-directories < initrd.img

#释放centos6.2系统的initramfs.img

1."gunzip initrd.img-2.6.27-7-generic.gz",得到一个未压缩的initrd.img-2.6.27-7-generic 

2. ”cpio -iv <initrd.img-2.6.27-7-generic",提取成功

#制作cpio格式的initrd(新2012年使用过的)

# cd /root/busybox-1.15.3/rootfs9260
# find . | cpio -H newc -o > ../initrd_cpio.img


#制作cpio格式的initrd(2009年制作的LFS的方式):

dd if=/dev/zero of=/tmp/rootfs bs=1k count=35000

losetup /dev/loop0 /tmp/rootfs

mkfs.ext2 –F –i 2000 /tmp/rootfs

mkdir /tmp/loop

mount –o loop /tmp/rootfs /tmp/loop

#然后将刚才建立的基本系统拷贝到/tmp/loop

cp /lfs/* /tmp/loop –arfp

find . | cpio –o –H newc | gzip –c > /tmp/initrd.img

 

centos7的initrd.img解压缩

红帽(Red Hat)从Enterprise Server 6.2 开始,启动镜像文件initrd.img 开始改用xz 工具进行压缩,这与以往版本是有区别的。


一、启动镜像initrd.img 文件

类RedHat 系统从vmlinuz 核心引导后,会读取initrd.img 启动镜像。该文件中包含驱动模块等信息,是非常重要的文件。不同版本使用的格式不同。

1.RHEL 4.0 版本

采用ext2 文件格式镜像,再通过gzip 压缩:



引用


# file initrd.img

initrd.img: gzip compressed data, from Unix, max compression

# mv initrd.img initrd.img.gz

# gunzip initrd.img.gz

# file initrd.img

initrd.img: Linux rev 1.0 ext2 filesystem data


2.RHEL 5.0 版本

采用cpio 打包镜像,再通过gzip 压缩:



引用


# file initrd.img

initrd.img: gzip compressed data, from Unix, max compression

# mv initrd.img initrd.img.gz

# gunzip initrd.img.gz

# file initrd.img

initrd.img: ASCII cpio archive (SVR4 with no CRC)


3.RHEL 6.2 版本

RHEL 6.0 - 6.2 都采用与RHEL 5.0 相同的格式进行打包,但从6.2版本开始,改用LZMA 进行压缩。详见:Release Notes for Red Hat Enterprise Linux 6.2 Edition 2。

如下:



引用


# file initrd.img

initrd.img: LZMA compressed data, streamed


※ 注意,若在低于RHEL 6.2 版本下执行file 命令,可能无法识别LZMA 压缩格式:



引用


# file initrd.img

initrd.img: data


这时,可把file 软件包升级到5.04-13.el6 即可。


二、xz 工具简介

xz 工具是LZMA 压缩算法的一个实现。具体可见:Wikipedia



引用


xz is a lossless data compression file format incorporating the LZMA2 compression algorithm. While xz can only support one file the convention is to bundle a file that is an archive itself, such as those created by the tar or cpio Unix programs. The original 7zip program implementing LZMA2 compression achieved small files (at the cost of speed compared to gzip or bzip2), but also created its own unique archive format which was Windows-centric and did not support Unix functionality; xz is essentially a stripped down 7zip with little archive format functionality, that compresses a single file (as opposed to 7zip's more complex capabilities like concatenating & compressing entire directories).

7-Zip supports xz since version 9.04 beta (stable since 9.20)


可见,Windows 下可使用7-Zip 打开.xz 文件。LZMA 算法比Gzip 算法压缩率更高。几个参数:



引用


# xz --help

Usage: xz [OPTION]... [FILE]...

Compress or decompress FILEs in the .xz format.


Mandatory arguments to long options are mandatory for short options too.


  -z, --compress      force compression

  -d, --decompress    force decompression

  -t, --test          test compressed file integrity

  -l, --list          list information about files

  -k, --keep          keep (don't delete) input files

  -f, --force         force overwrite of output file and (de)compress links

  -c, --stdout        write to standard output and don't delete input files

  -0 .. -9            compression preset; 0-2 fast compression, 3-5 good

                      compression, 6-9 excellent compression; default is 6

  -e, --extreme       use more CPU time when encoding to increase compression

                      ratio without increasing memory usage of the decoder


三、手动修改initrd.img 文件

解压:


# xz -dc initrd.img | cpio -id


压缩:


# find . | cpio -c -o | xz -9 --format=lzma > initrd.img



三、补充tar.lzma

由于LZMA 具有优秀的压缩率及占用资源少的特点,越来越多的工具采用lzma进行打包,后缀名为:tar.lzma。

对于Fedora 11 及以后的版本,可以使用下面的命令操作:

压缩


# tar cfv backup.tar.lzma a/dir --lzma


解压:


# tar xfv backup.tar.lzma --lzma



如果是CentOS 5.3 等老版本,需要安装独立的lzma 工具或用xz 进行: 

压缩:


# tar cv a/dir | lzma -c -z > backup.tar.lzma


解压(两个方式都可以):


# cat backup.tar.lzma | lzma -d | tar xv 

# xz -dc backup.tar.lzma | tar xvf -


 

光盘中的initrd.img

位置isolinux/initrd.img

1 解压缩

file后发现是xz文件

将initrd.img改名,改名为Initrd.img.xz

为什么要改名?因为不改名xz会叫唤,说你胡塞给我什么文件啊?我不解压缩

file initrd.img

mv initrd.img initrd.img.xz

unxz initrd.img.xz

解压缩完成。

结果:生成一个initrd.img,和原始文件比,这个文件是个cpio后的文件

2 cpio解出来

file initrd.img(第一步解压缩出来的名字)

发现是cpio

cpio -i -d < initrd.img

结果:生成所有文件系统,主要是usr, etc, lib等目录

 

2.6内核中的initrd.img采用cpio压缩,不再是2.4内核使用的ext2格式,无法使用mount -o loop 挂载。需要使用gunzip解压缩,然后再使用cpio解包

cp /boot/initrd-***.img initrd.img.gz

gunzip initrd.img.gz

mkdir initrd

mv initrd.img initrd

cd initrd

cpio -ivmd  ../initrd.new.img

gzip ../initrd.new.img

再将其改名拷贝至/boot目录,重启就可以观察修改后的效果。重启后无法成功引导。这是因为生成的initrd.img不对。解决如下:

生成initrd

find . | cpio -o -H newc | gzip > /mnt/sda1/boot/initrd.img-2.6.18-4-686

(注:-H newc 是必须的,否则内核会认为是ramdisk,而不是initramfs)

 

解压并重新打包 initrd.img

Extract

gunzip < /boot/initrd.img | cpio -i –make-directories

Repack

find ./ | cpio -H newc -o > initrd.cpio

gzip initrd.cpio

mv initrd.cpio.gz initrd.img

 

Uncompress:

mkdir initrd
cd initrd
zcat ../initrd.img | cpio -idmv


Compress:

find . | cpio -o -c | gzip -9 > ../initrd.img


 

initrd.img解压:

[root@CentOS5 ~]# mkdir /usr/src/initrd

[root@CentOS5 ~]# cp /boot/initrd-2.6.18-308.el5.img /usr/src/initrd

[root@CentOS5 ~]# cd /usr/src/initrd

[root@CentOS5 initrd]# ls

initrd-2.6.18-308.el5.img

[root@CentOS5 initrd]# mv initrd-2.6.18-308.el5.img initrd-2.6.18-308.el5.img.gz

[root@CentOS5 initrd]# gunzip initrd-2.6.18-308.el5.img.gz

[root@CentOS5 initrd]# cpio -i -d <  initrd-2.6.18-308.el5.img

[root@CentOS5 initrd]# rm -rf  initrd-2.6.18-308.el5.img 

[root@CentOS5 initrd]# ls

bin  dev  etc  init  lib  proc  sbin  sys  sysroot

 

initrd.img打包:

[root@CentOS5 initrd]# find . -print|cpio -o -H newc > ../initrd-2.6.18-308.el5.img

[root@CentOS5 initrd]# cd ..

[root@CentOS5 src]# gzip -9 initrd-2.6.18-308.el5.img 

[root@CentOS5 src]# mv  initrd-2.6.18-308.el5.img.gz  initrd-2.6.18-308.el5.img

[root@CentOS5 src]# ls

initrd-2.6.18-308.el5.img

 

*initrd.img打包也可采用如下命令:

[root@CentOS5 initrd]# find . | cpio --quiet -H newc -o | gzip -9 -n > ../initrd-2.6.18-308.el5.img.gz

 

[root@CentOS5 src]# mv  initrd-2.6.18-308.el5.img.gz  initrd-2.6.18-308.el5.img

[root@CentOS5 src]# ls

initrd-2.6.18-308.el5.img

 

解压Ubuntu的initrd.img的方法

Ubuntu的initrd.img可以在/boot中找到,通常文件名后面还跟有很长的一串版本号。

  为了保险起见,不直接操作原文件,而是把它复制到自己的家目(home)录中。如果你是用root帐号登录的,家目录就在/root中,如果是用wsxx登录的,家目录一般就在/home/wsxx中,通常登录之后自动就到了家目中。我们把initrd.img复制但家目中进行解压:

  cp /boot/initrd.img-2.6.15-ubuntu-r6 ./initrd.img.gz

  上面这个命令把/boot区中的文件复制到当前目录,并更名为initrd.img.gz。一方面改成短文件名好一点好操作,另一方面加上gz的后缀更清楚表明它原本就是一个gzip压缩出来的文件。http://www.ccthere.com/article/825480

  然后解压:

  gunzip initrd.img.gz

  也可以:http://www.ccthere.com/article/825480

  gzip -d initrd.img.gz

  两者结果是相同的,都是在当前目录得到一个解压后的initrd.img,原来的initrd.img.gz被删除掉了(这也许是linux整洁的优点)。

  现在这个更大的initrd.img要用cpio解开,成为一系列目录和文件。为了不与当前目录中现有的文件搞混,我们有必要新建一个目录,把initrd.img解压到新目录中去。将来把里面的文件修改好之后,还要把所有的目录文件再打包起来:http://www.ccthere.com/article/825480

  mkdir initrd #建立目录

  cd initrd  #进入目录

  cpio -i -d < ../initrd.img #解开上层目录中的initrd.img http://www.ccthere.com/article/825480

  因为已经进入到initrd中,../initrd.img表示上层目录中的initrd.img。

  现在就可以看到initrd中各目录中有很多新的目录和文件了。在这里我们可以窥视到ubuntu是如何装配起来的。可以对其中的内容进行修改了。

  http://www.ccthere.com/article/825480

  修改文本文件没有什么好多说的了。

  修改之后,就是压缩回去,用它来启动,检验是否可以正常启动,是否达到预期的修改目的。先用cpio打包:

  http://www.ccthere.com/article/825480

  find . | cpio -o -H newc > ../myinitrd.img #打包当前目录中的所有目录和文件,到上层目录中的myinitrd.img

  cd .. #回到上层目录

  gzip -9 myinitrd.img #gzip的最高级压缩http://www.ccthere.com/article/825480

  得到的myinitrd.img.gz就是新的Ubuntu启动文件了。

  做到这里,要有必要停下来看看一看,比较一下重新压缩之后的文件,是否和原来的initrd.img.gz差不多大小?都应该是4M多的文件。如果文件大小相差太多,可能就有问题。我用Ubuntu文件解压后在压缩回来,用不同的文件名,最后比较,大小完全一致,心里就踏实了。

  http://www.ccthere.com/article/825480

  再用Gentoo的initramfs文件进行同样方法的操作,2M多的文件解压再压缩回来,只剩下不到1K了,自己也不相信这是对的。

 

==================== End