編譯linux kernel及製作initrd ( by quqi99 )

quqi99發表於2013-01-27


                    編譯linux kernel及製作initrd ( by quqi99 )

作者:張華  發表於:2013-01-27
版權宣告:可以任意轉載,轉載時請務必以超連結形式標明文章原始出處和作者資訊及本版權宣告

( http://blog.csdn.net/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

顯示分割槽是從扇區(sector2048開始的,每個扇區是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





相關文章