工作過程簡述 在2.6kernel啟動時,它把rootfs作為它的第一個文件系統(tǒng)掛載(注意:這里的rootfs是真名!!!不是root filesystem的縮寫)。rootfs是一個特殊的tmpfs,,這個不能被刪除或者是unmounted。很多使用2.6內(nèi)核的系統(tǒng)通常都是掛載rootfs后什么都不做,,然后啟動另一個文件系統(tǒng)作為root filesystem,。但是,這個不能掩蓋rootfs存在的事實,,你可以“cat /proc/mounts” 來查看,,第一個掛載的肯定是rootfs。 rootfs被掛載后,,kernel立馬就解壓了那個用gzip壓縮的CPIO歸檔文件到rootfs,。每個2.6的內(nèi)核都會執(zhí)行這一步,但是默認那個壓縮檔是空的,,所以也就沒有往rootfs內(nèi)添加任何東西,。接著kernel會嘗試在rootfs去找尋/init,一旦找到init并執(zhí)行,,kernel也就完成了啟動工作,,然后便是剛剛執(zhí)行的init程序接管了接下來的工作。如果kernel沒法調(diào)用"/init"程序,可能就會回過頭去,,按照便準的做法去解析參數(shù)“root=”,,試圖找到另一個filesystem然后掛載它。 這里的使用initramfs是指,,提供一個/init程序給rootfs使用,我們可以通過兩種途徑實現(xiàn):使用編譯進內(nèi)核的cpio.gz檔案,,或者是一個獨立的cpio.gz檔案,。以前的initrd就是編譯一個獨立的檔案,很多使用initramfs的方式也是給它提供一個獨立的檔案,。
開工啦,! 為了不看起來那么乏味,我們嘗試通過一個看的著的例子來展示這個過程,。 唔,,我們還是把“hello world”作為第一個要放到initramfs中去的程序。事實上,,rootfs和其它的root filesystem并沒有什么區(qū)別,,如果你喜歡,你可以放/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); } 然后呢,,靜態(tài)編譯,,然后我們就不用考慮拷貝需要的庫過去了~ gcc -static hello.c -o hello 如果在命令行執(zhí)行這個小程序,它會打印hello world,,讓后停在那里,。你可以用ctrl-x讓它退出。如果是initramfs執(zhí)行這個程序,,我們會看到在boot messages的最后,,有個“hello world”被打印。 注意:如果是要放到你的開發(fā)板上去執(zhí)行,,記得使用你的交叉編譯工具,。打包的過程是和平臺無關(guān)的,但是二進制文件需要用目標系統(tǒng)的compiler,。 那么,,我們該怎樣把這個程序給kernel用內(nèi),?好吧,有四種基本方法:第一種是把cpio.gz作為一個獨立的包,,然后告訴bootloader它在哪里,;或者你可以用下面三種方法之一,把initramfs直接編譯進kernel里去,。
把cipo.gz作為獨立的檔案 很多人喜歡把它編譯進內(nèi)核里面去,,如果你樂意,你也可以這么做,。但是我們現(xiàn)在要用另一種方式,。我們可以使能內(nèi)核的initrd支持,然后用cpio.gz來代替ramdisk(initrd),。聰明的內(nèi)核會為我們自動檢測文件的類型,,然后把我們的壓縮包解壓放進rootfs;它不是創(chuàng)建一個ram disk,,這不會影響initramfs內(nèi)存效率高這一優(yōu)勢,。 因為external initramfs是在built-in initramfs之后執(zhí)行的,所以如果兩個檔案內(nèi)包含有同名的內(nèi)容,,獨立檔案會覆蓋掉built-in填進去去的東西,。這意味著,你不用修改kernel,,就可以update或者是ucstomize你的rootfs而不用換掉你的內(nèi)核,。 另外一個好消息是,這樣做你可以不用顧慮license的問題,!你可以在rootfs里面運行non-GPL的程序,,或者是給你的驅(qū)動提供non-GPL的firmware...額,編譯進內(nèi)核的話,,算是內(nèi)核的修改吧,?制作自己的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 按照傳統(tǒng)的使用initrd的方法,把上面生成的initramfs_data.cpio.gz放到該放的地方去(別問我要放哪里,,我也還不知道),,它就會在boot結(jié)束的地方為你打印一朵漂亮的“hello world”,然后等待一段時間并重啟,。 試試吧,! 如果它沒有工作,照例的你該查查initial ramdisk支持是不是有被選中,,然后看看你的init 程序是不是靜態(tài)鏈接的,,再看看它是不是又執(zhí)行權(quán)限,或者是名字是不是對的,。你可以用下面的命令來解壓任何的initramfs檔案到當前文件夾: zcat initramfs_data.cpio.gz | cpio -i -d -H newc --no-absolute-filenames 把initramfs編譯到內(nèi)核里面去 使用initramfs最簡單的方式,莫過于用已經(jīng)做好的cpio.gz把kernel里面那個空的給換掉,。這是2.6 kernel天生支持的,,所以,你不用做什么特殊的設(shè)置,。 kernel的config option里面有一項CONFIG_INITRAMFS_SOURCE(I.E. General setup--->Initramfs source file(s) in menuconfig),。這個選項指向放著內(nèi)核打包initramfs需要的所有文件。默認情況下,,這個選項是留空的,,所以內(nèi)核編譯出來之后initramfs也就是空的,也就是前面提到的rootfs什么都不做的情形,。 CONFIG_INITRAMFS_SOURCE 可以是一個絕對路徑,,也可以是一個從kernel’s top build dir(你敲入build或者是make的地方)開始的相對路徑。而指向的目標可以有以下三種:一個已經(jīng)做好的cpio.gz,,或者一個已經(jīng)為制作cpio.gz準備好所有內(nèi)容的文件夾,,或者是一個text的配置文件。第三種方式是最靈活的,,我們先依次來介紹這三種方法,。 1)使用一個已經(jīng)做好的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)指定給內(nèi)核一個文件或者文件夾 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來告訴內(nèi)核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.
總結(jié)一下 這四種給rootfs提供內(nèi)容的方式都有一個共同點:在kernel啟動時,一系列的文件被解壓到rootfs,,如果kernel能在其中找到可執(zhí)行的文件“/init”,,kernel就會運行它;這意味著,,kernel不會再去理會“root=”是指向哪里的,。 此外,一旦initramfs里面的init 進程運行起來,,kernel就會認為啟動已經(jīng)完成,。接下來,init將掌控整個宇宙!它擁有霹靂無敵的專門為它預(yù)留的Process ID #1,,整個系統(tǒng)接下來的所有都將由它來創(chuàng)造,!還有,它的地位將是不可剝奪的,,嗯哼,,PID 1 退出的話,系統(tǒng)會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./writing/ —————— |
|
來自: hginvent > 《embedded》