天天看點

initramfs的使用方法

原文位址:http://www.cnblogs.com/pied/archive/2013/01/29/2880718.html

這個是翻譯來的,原文位址:

http://www.landley.net/writing/rootfs-howto.html

怎樣使用initramfs

工作過程簡述

在2.6kernel啟動時,它把rootfs作為它的第一個檔案系統挂載(注意:這裡的rootfs是真名!!!不是root filesystem的縮寫)。rootfs是一個特殊的tmpfs,這個不能被删除或者是unmounted。很多使用2.6核心的系統通常都是挂載rootfs後什麼都不做,然後啟動另一個檔案系統作為root filesystem。但是,這個不能掩蓋rootfs存在的事實,你可以“cat /proc/mounts” 來檢視,第一個挂載的肯定是rootfs。

rootfs被挂載後,kernel立馬就解壓了那個用gzip壓縮的CPIO歸檔檔案到rootfs。每個2.6的核心都會執行這一步,但是預設那個壓縮檔是空的,是以也就沒有往rootfs内添加任何東西。接着kernel會嘗試在rootfs去找尋/init,一旦找到init并執行,kernel也就完成了啟動工作,然後便是剛剛執行的init程式接管了接下來的工作。如果kernel沒法調用"/init"程式,可能就會回過頭去,按照便準的做法去解析參數“root=”,試圖找到另一個filesystem然後挂載它。

這裡的使用initramfs是指,提供一個/init程式給rootfs使用,我們可以通過兩種途徑實作:使用編譯進核心的cpio.gz檔案,或者是一個獨立的cpio.gz檔案。以前的initrd就是編譯一個獨立的檔案,很多使用initramfs的方式也是給它提供一個獨立的檔案。

為了不看起來那麼乏味,我們嘗試通過一個看的着的例子來展示這個過程。

我們還是把“hello world”作為第一個要放到initramfs中去的程式。事實上,rootfs和其它的root filesystem并沒有什麼差別,如果你喜歡,你可以放/etc和/usr和/tmp和。。。然後還可以mount /proc 和/sysfs過去。但是這裡我們隻需要放/init過去。程式的最後我們使用sleeping而不是exiting,這主要是考慮如果PID 1的程式退出,kernel會panic,這會幹擾我們的視線。

#include 

int main(int argc, char *argv[])
{
  printf("Hello world\n");
  sleep(999999999);
}
      

然後呢,靜态編譯,然後我們就不用考慮拷貝需要的庫過去了~

gcc -static hello.c -o hello
      

如果在指令行執行這個小程式,它會列印hello world,讓後停在那裡。你可以用ctrl-x讓它退出。如果是initramfs執行這個程式,我們會看到在boot messages的最後,有個“hello world”被列印。

注意:如果是要放到你的開發闆上去執行,記得使用你的交叉編譯工具。打包的過程是和平台無關的,但是二進制檔案需要用目标系統的compiler。

那麼,我們該怎樣把這個程式給kernel用内?好吧,有四種基本方法:第一種是把cpio.gz作為一個獨立的包,然後告訴bootloader它在哪裡;或者你可以用下面三種方法之一,把initramfs直接編譯進kernel裡去。

把cipo.gz作為獨立的檔案

很多人喜歡把它編譯進核心裡面去,如果你樂意,你也可以這麼做。但是我們現在要用另一種方式。我們可以使能核心的initrd支援,然後用cpio.gz來代替ramdisk(initrd)。聰明的核心會為我們自動檢測檔案的類型,然後把我們的壓縮包解壓放進rootfs;它不是建立一個ram disk,這不會影響initramfs記憶體效率高這一優勢。

因為external initramfs是在built-in initramfs之後執行的,是以如果兩個檔案内包含有同名的内容,獨立檔案會覆寫掉built-in填進去去的東西。這意味着,你不用修改kernel,就可以update或者是ucstomize你的rootfs而不用換掉你的核心。

另外一個好消息是,這樣做你可以不用顧慮license的問題!你可以在rootfs裡面運作non-GPL的程式,或者是給你的驅動提供non-GPL的firmware...額,編譯進核心的話,算是核心的修改吧?制作自己的initramfs,隻是算是使用,你不用公布你的源代碼哦親!

那麼,怎麼制作cpio.gz檔案呢?一種方法是你用cpio和gzip指令自己來壓縮。當然,你也可以用kernel build來做這個,如果你覺得不是那麼麻煩的話。原意自己做的,隻需要敲下面這些代碼進去...

mkdir sub
cp hello sub/init
cd sub
find . | cpio -o -H newc | gzip > ../initramfs_data.cpio.gz
cd ..
rm -rf sub
      

按照傳統的使用initrd的方法,把上面生成的initramfs_data.cpio.gz放到該放的地方去(别問我要放哪裡,我也還不知道),它就會在boot結束的地方為你列印一朵漂亮的“hello world”,然後等待一段時間并重新開機。

試試吧!

如果它沒有工作,照例的你該查查initial ramdisk支援是不是有被選中,然後看看你的init 程式是不是靜态連結的,再看看它是不是又執行權限,或者是名字是不是對的。你可以用下面的指令來解壓任何的initramfs檔案到目前檔案夾:

zcat initramfs_data.cpio.gz | cpio -i -d -H newc --no-absolute-filenames

把initramfs編譯到核心裡面去

使用initramfs最簡單的方式,莫過于用已經做好的cpio.gz把kernel裡面那個空的給換掉。這是2.6 kernel天生支援的,是以,你不用做什麼特殊的設定。

kernel的config option裡面有一項CONFIG_INITRAMFS_SOURCE(I.E. General setup--->Initramfs source file(s) in menuconfig)。這個選項指向放着核心打包initramfs需要的所有檔案。預設情況下,這個選項是留白的,是以核心編譯出來之後initramfs也就是空的,也就是前面提到的rootfs什麼都不做的情形。

CONFIG_INITRAMFS_SOURCE 可以是一個絕對路徑,也可以是一個從kernel’s top build dir(你敲入build或者是make的地方)開始的相對路徑。而指向的目标可以有以下三種:一個已經做好的cpio.gz,或者一個已經為制作cpio.gz準備好所有内容的檔案夾,或者是一個text的配置檔案。第三種方式是最靈活的,我們先依次來介紹這三種方法。

1)使用一個已經做好的cpio.gz檔案

If you already have your own initramfs_data.cpio.gz file (because you created it yourself, or saved the cpio.gz file produced by a previous kernel build), you can point CONFIG_INITRAMFS_SOURCE at it and the kernel build will autodetect the file type and link it into the resulting kernel image.

You can also leave CONFIG_INITRAMFS_SOURCE empty, and instead copy your cpio.gz file to usr/initramfs_data.cpio.gz in your kernel's build directory. The kernel's makefile won't generate a new archive if it doesn't need to.

Either way, if you build a kernel like this you can boot it without supplying an external initrd image, and it'll still finish its boot by running your init program out of rootfs. This is packaging method #2, if you'd like to try it now.

2)指定給核心一個檔案或者檔案夾

If CONFIG_INITRAMFS_SOURCE points to a directory, the kernel will archive it up for you. This is a very easy way to create an initramfs archive, and is method #3.

Interestingly, the kernel build doesn't use the standard cpio command to create initramfs archives. You don't even need to have any cpio tools installed on your build system. Instead the kernel build (in usr/Makefile) generates a text file describing the directory with the script "gen_initramfs_list.sh", and then feeds that descript to a program called "gen_init_cpio" (built from C source in the kernel's usr directory), which create the cpio archive. This looks something like the following:

scripts/gen_initramfs_list.sh $CONFIG_INITRAMFS_SOURCE > usr/initramfs_list
usr/gen_init_cpio usr/initramfs_list > usr/initramfs_data.cpio
gzip usr/initramfs_data.cpio
      

To package up our hello world program, you could simply copy it into its own directory, name it "init", point CONFIG_INITRAMFS_SOURCE at that directory, and rebuild the kernel. The resulting kernel should end its boot by printing "hello world". And if you need to tweak the contents of that directory, rebuilding the kernel will re-package the contents of that directory if anything has changed.

The downside of this method is that it if your initramfs has device nodes, or cares about file ownership and permissions, you need to be able to create those things in a directory for it to copy. This is hard to do if you haven't got root access, or are using a cross-compile environment like cygwin. That's where the fourth and final method comes in.

3)使用configuration檔案initramfs_list來告訴核心initramfs在哪裡

This is the most flexible method. The kernel's gen_initramfs_list.sh script creates a text description file listing the contents of initramfs, and gen_init_cpio uses this file to produce an archive. This file is a standard text file, easily editable, containing one line per file. Each line starts with a keyword indicating what type of entry it describes.

The config file to create our "hello world" initramfs only needs a single line:

file /init usr/hello 500 0 0
      

This takes the file "hello" and packages it so it shows up as /init in rootfs, with permissions 500, with uid and gid 0. It expects to find the source file "hello" in a "usr" subdirectory under the kernel's build directory. (If you're building the kernel in a different directory than the source directory, this path would be relative to the build directory, not the source directory.)

To try it yourself, copy "hello" into usr in the kernel's build directory, copy the above configuration line to its own file, use "make menuconfig" to point CONFIG_INITRAMFS_SOURCE to that file, run the kernel build, and test boot the new kernel. Alternately, you can put the "hello" file in its own directory and use "scripts/gen_initramfs_list.sh dirname" to create a configuration file (where dirname is the path to your directory, from the kernel's build directory). For large projects, you may want to generate a starting configuration with the script, and then customize it with any text editor.

This configuration file can also specify device nodes (with the "nod" keyword), directories ("dir"), symbolic links ("slink"), named FIFO pipes ("pipe"), and unix domain sockets ("sock"). Full documentation on this file's format is available by running "usr/gen_init_cpio" (with no arguments) after a kernel build.

A more complicated example containing device nodes and symlinks could look like this:

dir /dev 755 0 0
  nod /dev/console 644 0 0 c 5 1
  nod /dev/loop0 644 0 0 b 7 0
  dir /bin 755 1000 1000
  slink /bin/sh busybox 777 0 0
  file /bin/busybox initramfs/busybox 755 0 0
  dir /proc 755 0 0
  dir /sys 755 0 0
  dir /mnt 755 0 0
  file /init initramfs/init.sh 755 0 0
      

One significant advantage of the configuration file method is that any regular user can create one, specifying ownership and permissions and the creation of device nodes in initramfs, without any special permissions on the build system. Creating a cpio archive using the cpio command line tool, or pointing the kernel build at a directory, requires a directory that contains everything initramfs will contain. The configuration file method merely requires a few source files to get data from, and a description file.

This also comes in handy cross-compiling from other environments such as cygwin, where the local filesystem may not even be capable of reproducing everything initramfs should have in it.

總結一下

這四種給rootfs提供内容的方式都有一個共同點:在kernel啟動時,一系列的檔案被解壓到rootfs,如果kernel能在其中找到可執行的檔案“/init”,kernel就會運作它;這意味着,kernel不會再去理會“root=”是指向哪裡的。

此外,一旦initramfs裡面的init 程序運作起來,kernel就會認為啟動已經完成。接下來,init将掌控整個宇宙!它擁有霹靂無敵的專門為它預留的Process ID #1,整個系統接下來的所有都将由它來創造!還有,它的地位将是不可剝奪的,嗯哼,PID 1 退出的話,系統會panic的。

接下來我會介紹其他一些,在rootfs中,init程式可以做的事。

Footnote 1: The kernel doesn't allow rootfs to be unmounted for the same reason the same reason it won't let the first process (PID 1, generally running init) be killed. The fact the lists of mounts and processes are never empty simplifies the kernel's implementation.

Footnote 2: The cpio format is another way of combining files together, like tar and zip. It's an older and simpler storage format that dates back to the original unix, and it's the storage format used inside RPM packages. It's not as widely used as tar or zip because the command line syntax of the cpio command is unnecessarily complicated (type "man 1 cpio" at a Linux or Cygwin command line if you have a strong stomach). Luckily, we don't need to use this command.

Footnote 3: The kernel will always panic if PID 1 exits; this is unrelated to initramfs. All of the signals that might kill init are blocked, even "kill -9" which will reliably kill any other process. But init can still call the exit() syscall itself, and the kernel panics if this happens in PID 1. Avoiding it here is mostly a cosmetic issue: we don't want the panic scrolling our "Hello World!" message off the top of the screen.

Footnote 4: Statically linking programs against glibc produces enormous, bloated binaries. Yes, this is expected to be over 400k for a hello world proram. You can try using the "strip" command on the resulting binary, but it won't help much. This sort of bloat is why uClibc exists.

Footnote 5: Older 2.6 kernels had a bug where they would append to duplicate files rather than overwriting. Test your kernel version before depending on this behavior.

Footnote 6:User Mode Linux or QEMU can be very helpful testing out initramfs, but are beyond the scope of this article.

Footnote 7: Well, sort of. The default one is probably meant to be empty, but due to a small bug (gen_initramfs_list.sh spits out an example file when run with no arguments) the version in the 2.6.16 kernel actually contains a "/dev/console" node and a "/root" directory, which aren't used for anything. It gzips down to about 135 bytes, and might as well actually be empty. On Intel you can run "readelf -S vmlinux" and look for section ".init.ramfs" to see the cpio.gz archive linked into a 2.6 kernel. Elf section names might vary a bit on other platforms.

Some useful stuff is here:http://www.landley.net/writing/

繼續閱讀