kernel: OS 本身,會被 boot loader 讀進來
initrd: bootloader 會 load 進來,避免一些 driver 在 root filesystem 尚未 mount 時,無法取得
==> initrd 應該還是 kernel load 進來,因為 bootloader 應該不會管到 OS 該如何處理 initrd(否則其他 OS 該怎辦?),所以是 kernel load initrd 進來,並且執行 linuxrc 程式。
Kernel:主要是處理真正 OS 要做的事情
initrd:用來解決雜七雜八,但好像需要 kernel 要執行的東西,但為讓 kernel 更為乾淨,將這些東西移到 initrd 的 file system 中,該 file system 的 init 為第一個執行檔,可用來 mount 真正的 root file system。
root filesystem:OS 所需要的 file system。如:C lib 等
Ramdisk 是其中一種在記憶體上實現檔案系統的方法,一般嵌入式系統當中通常沒有硬碟的存在,但是有作業系統就會有檔案系統,因此嵌入式系統就要從記憶體裡面分出一塊區域,來做為檔案系統之用,這個區域就叫Ramdisk。
根檔案系統(Root File System、RFS)
在嵌入式系統中,並不像PC 有著大容量硬碟存在,需利用Ramdisk 模擬的方法執行系統。
Ramdisk 是利用既有的RAM 或Flash,模擬一段磁碟空間,放置Root File System 供程式執行用。
當kernel 開機完成,隨即進入RFS 內執行程式,根檔案系統內容主要如下:
/bin:放置命令檔之目錄。
/etc:放置設定檔之目錄。
/dev:放置硬體節點檔之目錄。
/lib:放置Glibc 之目錄。
/mnt:用於暫時掛載檔案系統之目錄。
/proc:Kernel 虛擬的檔案系統,提供使用者方便
觀看行程資訊與硬體狀況。
做initramfs開機時的錯誤
原因是"/"下需要有init,可以link到/sbin/init
==================================
Linux 内核启动系统时,它必须找到并执行第一个用户程序,通常是 init。
用户程序存在于文件系统,故 Linux 内核必须找到并挂载上第一个(根)文件系统,方能成功开机。
通常,可用的文件系统都列在 /etc/fstab,所以 mount 可以找到它们。但 /etc/fstab 它本身就是一个文件,存在于文件系统中。找到第一个文件系统成为鸡生蛋蛋生鸡的问题,而且为了解决它,内核开发者建立内核命令列选项 root=,用来指定 root 文件系统存在于哪个设备上。
十五年前,root= 很容易解释。它可以是软盘或硬盘上的分区(Partition)。
如今 root 文件系统可以存在于各种不同类型的硬件(SCSI, SATA, flash MTD) ,或是由不同类型硬件所建立的 RAID 上。
它的位置随着不同启动而不同,像可热插拔的 USB 设备被插到有多个 USB 孔的系统上 - 当有多个 USB 设备时,哪一个是正确的?
root 文件系统也可能被压缩(如何?),被加密(用什么 keys?),或 loopback 挂载(哪里?)。它甚至可以存在外部的网络服务器,需要内核去取得 DHCP 地址,完成 DNS lookup,并登入到远程服务器(需账号及密码),全部都在内核可以找到并执行第一个 userspace 程序之前。
如今,root = 已没有足够的信息。即使将所有特殊案例的行为都放进内核也无法帮助设备列举,加密,或网络登入这些随着系统不同而不同的系统。
更糟的是,替核心加入这些复杂的工作,就像是用汇编语言写 web 软件 :可以做到,但使用适当的工具会更容易完成。核心是被设计成服从命令,而不是给命令。
为了这个不断增加复杂度的工作, 核心开发者决定去寻求更好的方法来解决这整个问题。
解决方法
Linux 2.6 核心将一个小的 ram-based initial root filesystem(initramfs) 包进内核,且若这个文件系统包含一个程序 init,核心会将它当作第一个程序执行。此时,找寻其它文件系统并执行其它程序已不再是内核的问题,而是新程序的工作。
initramfs 的内容不需是一般功能。若给定系统的 root 文件系统存在于一个加密过的网络块设备,且网络地址、登入、加密都存在 USB 设备 "larry" (需密码方能存取)里,系统的 initramfs 可以有特殊功能的程序,它知道这些事,并使这可以运作。
对系统而言,不需要很大的 root 文件系统,也不需要寻址或切换到任何其它 root 文件系统。
这跟 initrd 有何不同?
Linux kernel 已经有方法提供 ram-based root filesystem,initrd 机制。对 2.4 及更早的 kernel 来说,initrd 仍然是唯一的方法去做这一连串的事。但 kernel 开发者选择在 2.6 实现一个新的机制是有原因的。
ramdisk vs ramfs
1. ramdisk (如 initrd) 是 基于ram的块设备,这表明它是一块固定大小的内存,它可以被格式化及挂载,就像磁盘一样。
这表明 ramdisk 的内容需先格式化并用特殊的工具(像是 mke2fs 及 losetup)做前置作业,而且如同所有的块设备,它需要文件系统驱动程序在执行时期解释数据。这也有人工的大小限制不论是浪费空间(若 ramdisk 没有满,已被占用的额外的内存也不能用来做其它事)或容量限制(若 ramdisk 满了,但其它仍有闲置的内存,也不能不经由重新格式化将它扩展)。
但 ramdisk 由于缓冲机制(caching)实际上浪费了更多内存。Linux 被设计为将所有的文件及目录做缓存,不论是对块设备的读出或写入,所以 Linux 复制数据到 ramdisk及从 ramdisk 复制数据出来,page cache 给 file data 用,而 dentry cache 给目录用。ramdisk 的下面则伪装为块设备。
几年前,Linus Torvalds 有一个巧妙的想法:Linux 的缓存是否可以被挂载一个文件系统?只要保持文件在缓存中且不要将它们清除,直到它们被删除或系统重新启动?Linus 写了一小段程序将缓存包起来,称它为 ramfs,而其它的 kernel 开发者建立一个加强版本称为 tmpfs(它可以写数据到 swap,及限制挂载点的大小,所以在它消耗完所有可用的内存前它会填满)。initramfs 就是 tmpfs 的一个实例。
这些基于ram 的文件系统自己改变大小以符合数据所需的大小。增加文件到 ramfs(或增大原有的文件)会自动配置更多的内存,并删除或截去文件以释放内存。在块设备及缓存间没有复制动作,因为没有实际的块设备。在缓存中的只 是数据的复制。更好的是这并不是新的程序代码,而是已存在的 Linux 缓存程序代码新的应用,这表示它几乎没有增加大小,非常简单,且基于已经历测试的基础上。
系统使用 initramfs 作为它的 root 文件系统甚至不需要将文件系统驱动程序内建到 kernel,因为没有块设备要用来做文件服务器。只是存在内存中的文件罢了。
initrd vs initramfs
底层架构的改变是 kernel 开发者建立一个新的实现的理由,但当他们在那里时他们清除了很多不好的行为及假设。
initrd 被设计为旧的 root= 的 root 设备检测程序代码的前端,而不是取代它。
它执行 /linuxrc,这被用来完成设定功能(像是登入网络,决定哪个设备含有 root 分区,或用文件做为 loopback 设备),告诉 kernel 哪个块设备含有真的 root 设备(通过写入de_t 数据到 /proc/sys/kernel/real-root-dev),且回传给 kernel,所以 kernel 可以挂载真的 root 设备及执行真的 init 程序。
这里假设“真的根设备”是块设备而不是网络共享的,同时也假设 initrd 自己不是做为真的 root 文件系统。kernel 也不会执行 /linuxrc 而做为特殊的进程(ID=1),因为这个 process ID(它有特殊的属性,像是做为唯一无法被以 kill -9 的 process) 被保留给 init,kernel 在它挂载真的 root 文件系统后会等它执行。
用 initramfs,kernel 开发者移除所有的假设。当 kernel 启动了在 initramfs 外的 /init,kernel 即做好决定并回去等待接受命令。
用 initramfs,kernel 不需要关心真的 root 档案系统在哪里,而在 initramfs 的 /init 被执行为真的 init,以 PID 1。(若 initramfs 的 init 需要不干涉特别的 PID 给其它程序,它可以用 exec() 系统呼叫,就像其它人一样)
总结
传统的 root= kernel 命令列选项仍然被支持且可用。但在开发支持initial RAM disk支持内核时,提供了许多优化和灵活性。
译者注
1. 查看initramfs的内容
# mkdir initrd
# cd intrd
# cp /boot/initrd.img initrd.img
# gunzip initrd.img
# cpio -i --make-directories < initrd.img
#
2. 创建initramfs
2.a. mkinitramf
# mkinitramfs -o /boot/initrd.img 2.6.2
Note: 2.6.25是需要创建initramfs的kernel版本号,如果是给当前kernel制作initramfs,可以用uname -r查看当前的版本号。提供kernel版本号的主要目的是为了在initramfs中添加指定kernel的驱动模块。mkinitramfs会把 /lib/modules/${kernel_version}/ 目录下的一些启动会用到的模块添加到initramfs中。
2.b. update-initramfs
更新当前kernel的initramfs
# update-initramfs -u
在添加模块时,initramfs tools只会添加一些必要模块,用户可以通过在/etc/initramfs-tools/modules文件中加入模块名称来指定必须添加的模块。
命令:mkinitramfs, update-initramfs
3. mkinitcpio
在Arch Linux中,有一个新一代的initramfs制作工具。相对于老的mkinitrd和mkinitramfs,它有以下很多优点。查看详细《使用mkinitcpio》。
参考链接:
精通initramfs构建 http://linuxman.blog.ccidnet.com/blog-htm-do-list-uid-60710-type-blog-dirid-14402.html
制作initramfs镜像 http://www.diybl.com/course/6_system/linux/Linuxjs/200888/135080.html
=====================================================
I. 隨手筆記
note0002
initrd(initial RAM disk) 何時被 load
在 kernel thread init 生出 user thread init 前就會先將 initrd 給載入, 這樣才有第一個 root file system。Kernel thread init 中有一段 code:
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; prepare_namespace(); }called functions:
prepare_namespace()@.../init/do_mounts.c ↓ initrd_load()@.../init/do_mounts_initrd.c ↓ handle_initrd()@.../init/do_mounts_initrd.c在 handle_initrd() 中可看到會產生一個 kernel thread 'do_linuxrc',以執行 /linuxrc 這個 script。
另外,initrd 與真正的 root file system 的先後關係看參考下圖 (取自 http://www.almesberger.net/cv/papers/ols2k-9.pdf)及 .../Document/initrd.txt。
note0001
PID = 1 的 process 如何被產生
booting 流程正式進入 linux kernel,並執行到 start_kernel() 後,在 start_kernel() 裏的最後一步會執行 rest_init()。在 rest_init() 中會 呼叫kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);
以產生另一個 kernel thread,也就 init。注意第一個參數,實際上這個 init 是 定義在同一個檔案裏的一個 function:static int init(void * unused)。而在 init() 的最後幾行會看到以下片斷:
if (execute_command) run_init_process(execute_command); run_init_process("/sbin/init"); run_init_process("/etc/init"); run_init_process("/bin/init"); run_init_process("/bin/sh"); panic("No init found. Try passing init= option to kernel.");
run_init_process() 會嘗試執行這個四個執行檔。如果執行成功,就不會 return。 。換句話說,init()@.../init/main.c 這個 thread 會被上面四個執行檔之一給取代 掉。
所以到此時,系統中會存在二個 thread:一是 start_kernel,一是 /sbin/init (或其餘三個執行檔之一),也就是 user space 的第一個 processs (PID 為 1)。 其中,start_kernel() 最終會進入 cpu_idle()@.../arch/arm/kernel/process.c 。注意裏頭有一個無窮迴圈,start_kernel() thread 就會在這裏不斷 loop。
start_kernel --> cpu_idle() --> loop forever | | spawned by kernel_thread() | |--> init() --> /sbin/init (1st user space process)
==========================================================
Kernel與initrd的基本建造
之前的Kernel或者initrd,我們都是拿人家已經做好的來使用而以,所以我們要來講如何用工具自己創造自己合適的kernel跟initrd.Kernel編譯的注意事項
目前2.6的比較簡單,不用make depmod; make clean。modprobe是用來裝module的,所有的kernel模組都在/lib/modules/`uname -r`/kernel/下,以xxx.ko存在。系統一起來的時候,通常/etc/init.d/下的script會去叫modprobe,他會去下面兩個地方找相關設定
- .confg 這是所有編譯的選項,可直接改裡面的參數。
- 小心,必須不可以有重複的CONFIG_XXX存在,因為comment掉的後面會蓋掉前面的。例如
這樣IGB這個module是不會被compile的,所以用make menuconfig是比較簡單的。- kernel debug info不要編譯,會使kernel跟modules變得超級肥大。
- make menuconfig 選擇編譯選項的Text界面,需要安裝libcurse跟tcl,也可以用存檔就是存.config
- make bzImage 建立壓縮的kernel。make -j 8可以平行處理8個 job。
- make modules 編譯各模組
- make modules_install 把各模組放到/lib/modules/`uname -r`/下面去,並且建立module間的dependacny
- depmod -a 這會重新尋找目前kernel下的所有模組,重新建立彼此的dependancy,如果你是third party的driver,基本上會放在/lib/modules/`uname -r`/extra底下,這時必須重建modules.dep。
- /etc/modprobe.conf
- /etc/modprobe.d/xxx
modprobe.conf與modprobe.d下的檔案設定形式與格式是一樣的。
- alias wildcard modulename : 這通常是compatible問題,以前模組名稱變化,或者長名字變短名字。
- alias usbdevfs usbcore
- alias nfs* nfs
- modprobe nfs4時,其實是會帶上nfs這模組。
- options modulename option
- options loop max_int=16
- modprobe loop時,會自動帶上max_int=16這個參數給loop這模組。
- install modulename command
- "install probe-ethernet /sbin/modprobe e100 || /sbin/modprobe eepro100"
- modprobe probe-ethernet 將會先找e100再找eepro100
- remove modulename command 跟install一樣只是是rmmod發生作用。
- include filename 可以include其他的檔案。
另外debian的有一個/etc/modules這個不標準的設定,因為本來模組driver由udev來驅動,但有的模組不是硬體,所以udev無法驅動,這只有從/etc/init.d/下自己驅動,但每一個都要這麼作就要重複寫一樣的script,所以debian有一個/etc/modules來驅動純software的模組像loop這種。kernel的參數傳遞
在grub/lilo/syslinux等Linux Loader都可以傳某些參數過去,因為這些OS dependent的loader是懂得Linux的特殊記憶體位置在做什麼的。所以像syslinux裡面可以用append來傳進去給kernel。/proc/cmdline裡面可以看到這些傳過去的參數,這些參數如果對kernel有意義,則kernel會拿進來用,如果對kernel沒有意義,則kernel會把他們放進環境變數裡面,不過最保險的方法還是去讀/proc/cmdline。因為如果不是很了解kernel,可能傳進去的剛好是某個kernel要的參數名字,那就不會出現在環境變數中了。 一些基本的kernel參數:
- root
- initrd
- init
- nfsroot
initrd探討
在作業系統沒有起來前,有些狀況是需要一個小的OS環境來作些工作的。例如全新安裝,crash急救,網路開機等等。這時我們只需要一點點的工具,然後load到記憶體中,把某段記憶體當成disk使用,就有一個小的環境跟工具可以使用了。這就是initrd (init ram disk)。這裡面其實就是一個小的暫時root file system。在boot的時候,最重要就是要掛上root file system,所以initrd裡面最重要的就是要確定最後真正root device是哪一個,並且在這小小的檔案中把負責啟動root device的driver load進來。最後再掛到真正的root去把其他的driver跟init完成。所以如果當時file system是有切割很多partiitions的,例如/var /home等等,這些都是在真實root上的/etc/fstab下設定的,只要最後進到真正的root,那麼就可以執行真正root下的init了。 root device的可能性所以如果你的root device是IDE,那initrd裡面有driver了就會load進來,這時候掛上真的硬碟上的root file system,然後再一一掛上其他的driver,所以root device的driver跟其他device的driver掛上的時間點是不一樣的。root device掛上的是在initrd裡面的/lib/modules/`uname -r`/,其他的是在真正root下的/lib/module/`uname -r`/。
- IDE/SCSI - 這會去load driver module
- PXE NFS - 這要先把nfs root編進kernel
- SAN boot - 跟SCSI卡的driver大同小異,一般使用者就不用管firmware怎麼implement的。
2.4與2.6的不同
最後雖然都是initrd但是2.4 2.6的格式與init的方法不太一樣。2.4的格式必須是一個檔案系統,2.6的支援檔案系統跟cpio形式的。都可以用gzip壓起來。這不僅僅是格式不同,在kernel內部的處理方式也不同。2.4的initrd是一個init ram disk, 是一個block device上掛載著一個一般的file system。而2.6的是一個initramfs是一個rootfs的image,rootfs他是藏在kernel裡面的一個小file system就跟ext2,ext3...一樣有目錄檔案讀寫等等,不須額外編譯 ,在filesystems的link list結構裡面就自動帶有這麼一個filesystem,就像網路device自動帶有lo這個interface一樣。rootfs是base在以前的ramfs上的,在kernel裡面的fs下面有一個ramfs這個filesystem,現在裡面init時自動會帶上一個rootfs。cpio的initramfs image展開來就load到rootfs的相對記憶體上了。file system格式的initrd (2.4 版)
- 使用一個ramdisk /dev/ram0來當作這個file system的block device
- 這個file system必須在compile kernel時就要進去,不能是module,在debian通常會用cramfs。
- 執行/linuxrc
- /linuxrc結束時,會回到kernel,必須先用pivot_root把root掛上,kernel會認為真正的root已經掛上去了,直接執行/sbin/init為第一個user space process,
- 使用pivot_root來改變整個系統root到真正root上面去。
cpio格式的initramfs (2.6 版)
- 此種initrd為使用一個kernel內部rootfs的initramfs image,這是定義在kernel內部一個固定的address下。kernel會展開initrd放到特定的地方去。
- 所以不需要特別編譯一個中介的file system到kernel內。
- 執行/init
- /init就是第一個user space process,不再回到kernel。必須使用klibc的run-init 或者busybox的switch_root跳到真實root file system上。並且自己執行/sbin/init。
- rootfs的initramfs image內容無法umount,唯一能做的是delete掉所有內容來釋放記憶體。
- 所以傳統pivot_root命令工具使用上很麻煩,必須自己implement新的流程完成,或使用新的工具,busybox提供switch_root或者klibc提供run-init命令。
解開initrd
2.4的initrd有的mount不會自動解開gzip,所以要自己先解開。另外file system也要看有沒有load進來,像debian kernel 2.4都喜歡用cramfs來建造initrd,這就要把這個檔案系統編譯進來。
- kernel 2.4的initrd: # mount -o loop initrd.img /mnt/initrd
- kernel 2.6的initrd: # gzip -dc ../initrd.gz | cpio -id --no-absolute-filenames
我們用debian的CDROM裡面的initrd來解開,解開後我們觀察/lib/modules/`uname -r`的driver會發現他只有放block device的跟檔案系統而已,這是因為我們只需要mount root所需要的東西就可以了。建造initrd
建造initrd有兩種狀況,一種是要boot原本已有的系統,這需要了解不同distro的需求,好在各distro已經有script幫我們了,只要一行命令就可,另一種是我們想要自己的distribution或者我們要做自己的急救開機片,或者我們embadded系統上開機要使用,這時要自己建造一個。distros的initrd建造
不同的distribution有他們自己的工具創造initrd,主要是要符合自己的init程序,各家的內定init方法雖然大同小異但不是完全一樣的。還有initrd內放的軟體內容也不一樣的。好像大家的command都是mkinitrd,不過有些選項可能不太一樣。新的建造2.6的cpio/initramfs形式的initrd命令,像debian/ubuntu的已經變成mkinitramfs
- Debian : # mkinitrd -o xxx.img 2.6.16
- Fedora : # mkinitrd xxx.img 2.6.18-164.el5
- Ubuntu : # mkinitrd -o xxx.img 2.6.16
- Centos : # mkinitrd xxx.img 2.6.18.1
- mkinitramfs /boot/initrd-2.6.18-amd64.img 2.6.18
自己的initrd
這個主要是要有一個假的root, fakeroot, 然後在下面放很多將來開機用到的檔案,然後用cpio跟gzip捆綁起來一個檔案,initrd.img就好。這裡面的關鍵點有兩個,一個是init這隻script要做哪些重要的事情,一個就是做這些事情該放的檔案有哪些。基本上新的都已經換到cpio形式的了,所以只說明cpio /init的。 最簡單的/init就是執行一個shell,由initramfs的內容當成root filesystem,把該load的硬體driver帶上,可以把mp3的播放軟體放裡面就是小mp3播放embedded system。但是真正有用的話還是要去mount一個大root filesystem來得到更多的應用。kernel 2.6 /init要做的事情有:現在我們做一個自己的initrd並且用virtual machine來測試他,主要是udev, 尋找root device的kernel modules有哪些跟所需要的工具程式。
- mount proc sysfs tmpfs
- 偵測硬體並且load root device所需drivers,例如raid, SAN HBA卡等等。就是正常Linux上的工具,現在通常用udev了。
- 轉換到到真正的root file system並且從此以後使用這個root,這跟chroot不一樣的是chroot只是單一process的改變,而pivot_root是整個系統都變成新root。以往pivot_root是呼叫pivot_root的process繼續留在舊的root,系統變成新root,這個呼叫pivot_root的通常是個shell script,他會作一些舊root的 clean up工作,最後才chroot到新root去。
- 清掉initrd的記憶體,以往2.4 initrd是改成新root後,還要返回kernel,由kernel去呼叫新root的/sbin/init,並且umount掉initrd,但是initramfs的initrd裡面的/init最後自己要變成root後就不返回kernel了,且initramfs是內部的一塊記憶體空間,無法umount,只能delete掉所有檔案。新工具busybox的switch_root或者klibc的run-init可以簡單一行命令的作這些事,不再用pivot_root了。
- switch_root或run-init的stdin/stdout/stderr都需導向新root的/dev/console去。
- 通常到新root去,從此以後就呼叫真正的BSD init或者sysV init來啟動整個系統了。
- udev
- 可以解開系統上的/boot下的initrd, copy /lib/udev, /etc/udev。
- 也可用package的列出檔案方法找出,例如debian的用dpkg --listfiles udev就可以找出所有裝在系統上的bin跟lib有哪些
- 用ldd 找出binary所需的library,copy到fakeroot去。
- udev使用的modprobe可能有特別的參數,所以要確定他使用的工具執行起來是否是對的,busybox的有時候是不能用的。
- 在/lib/udev/rules.d下面去grep program這個字串,會有每個rule偵測硬體所需的程式,一樣要copy出binary跟library.
- kernel moduls
- 用系統上的/boot下面的initrd,2.6的用gzip -dc initrd.img | cpio -i 解開
- 然後copy裡面的lib/modules下的檔案到fakeroot去。
- 工具程式
- 可以用busybox,也可以copy系統上的mount, mknod, dhclient...等等東西
- copy系統上的跟udev的一樣,用ldd 去找出bin下面的工具所需的library,copy到fakeroot去。
下面簡單例子用busybox還有udev來作工具。其中注意的是init script 暫時root的目錄檔案結構 好了,我們用 $ cd fakeroot $ find . | cpio -o -H newc | gzip -9 > ../initrd.img 建造一個新的initrd.img。最後把他跟我們編譯的kernel放進到isolinux.cfg裡面或者pxeboot的pxelinux.cfg/default裡面就好。用VM開機。當然我們也可以不需要轉到一個真的root file system而繼續使用原本的rootfs就好,這通常可以用來upgrade firmware或者全新安裝一個Linux。
- busybox的switch_root需要是PID = 1的狀態,所以必須用exec來執行。
- 我假設已經有一個建好的root在sda2上了。而且有/sbin/init在sda2上,其實就是用本來已有的linux系統來玩就可以了。
udev偵測硬體
在上面偵測硬體時,我們用上了現在新的udev工具,udev是user dev的意思,由於後來硬體都有所謂的熱插拔,或者像usb device可以任意時間內裝上卸除系統,所以udev可以根據硬體變化動態產生/dev/下的node。這工具的使用可以讓我們適用各種硬體,帶上所需driver,不是一開機後死死的用modprobe去load特定的硬體而已。initrd 主要要帶上的是root filesystem所在的 block device驅動程式而已,所以我們還是可以用udev來load drivers。udev的使用為start一個udevd這個daemon,他會監視硬體變化的event,這是由kernel丟出來,然後udevd會去讀的,放到一個 queue去,然後用udevadm trigger產生cold plug,來一次性的處理這個event queue裡面的硬體,處理方式根據udev rules來處理,udev source code裡面本來就有寫好的一些rules,我們可以用這個來擴充我們的硬體rules.udev編譯
目前他有一些dependancy,都是kernel.org下的那些相關tools,請看http://www.kernel.org/pub/linux/utils
- 編譯一些工具
- 在 http://www.kernel.org/pub/linux/utils 下面有很多關於kernel的utilities,去kernel/hotplug下面下載udev,基本packages目前為
- util-linux
- module-init-tools, 這會被libkmod取代,裡面就是modprobe...這些東西
- usbutils (需要libusb)
- udev (新版的請disable introspection)
編譯時要小心的是這是將來用在root的上面的,所以library相關參考位置都在/ 上,如果真的要作一份全新的,還是用Linux From Scratch先建立一個compile的tool chain, 再chroot去compile. udev compile時的configure不要忘了用configure --prefix=/usr來處理,configure --help可以看到一些compile選項。compile完後,新的udev的工具程式有udevd這個daemon跟udevadm這個管理工具程式。以及extra目錄下的一些工具。extra下工具主要是用來識別硬體Id的,這可以用在udev rules裡面,當看到某個id就去驅動某個driver。
- udevd --daemon :使udevd成為daemon
- udevadm command
- udevadm trigger : 產生cold plug, 由於機器內部的硬體並沒有真正hot plug, 所以產生一個code plug來通知udev queue.
- udevadm settle true : 檢查event queue裡面是不是真的全部結束
- scsi_id
- vol_id
- pci_id
不過新版的有的已經拿掉了,加上其他的工具,像是pci-db, usb-db之類的。udev rules
udev會從/lib/udev/rules.d/下讀取default的rules,再從/etc/udev/rules.d下讀取custom的rules還有/dev/.udev/rules.d下讀取暫時的rules。 基本rules是一對key=value pair,用來表示符合的條件 其中比較重要的一些key就是基本上這是跟kernel內部丟出來的資訊有關的,如果不是kernel developer可以先試著用
- KERNEL
- kernel 內部的event device 名字,例如eth0, hda, sda, sg0...這些在/dev/下原本的device名字或者網路interface名
- SUBSYSTEMS
- kernel 內部subsystem的event device名字,例如block, scsi, usb...等等這種device的抽象分類系統,
- ATTR
- /sys下面的資訊的讀取
- SYMLINK
- 在/dev/下給一個symbolic link名字,通常像/dev/cdrom就是這樣來的
- RUN
- 這就是程式執行發動者,通常modprobe就是在RUN這裡面
- IMPORT(program)
- 這是使用一個外部程式來得到device的properties。例如ata_id,blk_id等等來找到device的ID。
下面的目錄得到一些資訊,例如udevadm info -a -p /sys/block/sda
- udevadm info -a -p /sys/
在以前本來是要針對每一個硬體來modprobe,後來的新版的udev對於module會去掃出來硬體device的MODALIAS,在/sys/devices/下面可以掃出所有的modalias,然後在rule中加一個modprobe $MODALIAS就好了。這個modalias可以在/lib/modules/`uname -r`/modules.alias下面看到,也可以modinfo去看一個特定的module會看到,這info是由kernel根據driver硬體產生的。這個modalias大概長這樣pci:v00008086d00001C26sv00001458sd00005006bc0Csc03i20只要去modprobe他也可以load上這個driver.Busybox與klibc
在創建自己的initrd時,我們用了很多工具是一般用的,像mount, mkfs...,這些東西都很肥大,因為他們是標準工具,有很多種選項,但是其實跟微軟的office一樣,大部分時候我們只用到一小部分功能,所以有一個瑞士刀Busybox,有很多功能,甚至有web server的功能,編譯出來的code很小,我們可以用在embedded系統上. Busybox的選項跟kernel一樣在make時要設定,$ make menuconfig$ make就可以產生我們想要的busybox了。而編譯執行檔需要的library,可以選擇最小化的klibc,這個程式庫可以提供大部分的功能,也提供一些工具程式,像是cpio, dash, fstype, mkdir, mknod, mount, nfsmount, run-init, etc.等等可以來mount第一個root filesystem。gcc 多加-Os這個選項。 自己的mp3 player 所以其實我們也可以做自己的mp3 player這樣的embadded system,只要我們建好vmlinuz,跟initrd,只是這個initrd裡面有init,然後他的driver module裡面有所有的相關硬體dirver,最後擺上一個播放mp3 player的軟體,機器boot之後,就進到這個mp3 player就可以了。當然如果真要量產的話,user界面要有一些按鈕,這些按鈕按一按,必須讓相對應的mp3 player軟體作業。kernel的header有的資料結構是user space的程式有興趣的。可以make headers_install來安裝,不過通常系統中可能都有了,會放到/usr/include/linux下面。
沒有留言:
張貼留言