linux文件系統啟動流程、啟動腳本
下面是一張Linux啟動流程圖:
在了解啟動流程之前,我們應該先知道系統的幾個重要腳本和配置文件,他們對應的路徑為:
1. /sbin/init
2. /etc/inittab
3. /etc/rc.d/rc.sysinit
4. /etc/rc.d/rcN.d //這是幾個文件夾N代表數字1,2,3,4..
5. /etc/fstab
1.關於/sbin/init與/etc/inittab
關於/sbin/init ,它是一個二進制可執行文件,為系統的初始化程序,而/etc/inittab是它的配置文件,我們可以通過/etc/inittab來一睹 它的功能, 里面的內容是一種固定的文本格式,id:runlevels:action:process
我們來通過它的內容來學習它之前,先了解寫運行級別的分類(0-6):
0: 關機 half
1: 單用戶模式 singel user
2: 多用戶模式 multi user , 不提供nfs服務 without nfs
3: 完全多用戶字符模式 full multiuser text mod
4: 系統預留 officially undefined
5: 圖形登錄界面 graphical login
6: 重啟 reboot
- id:3:initdefault: //這里定義linux的啟動時的運行級別,可以看到我主機的啟動級別是3
- # System initialization.
- si::sysinit:/etc/rc.d/rc.sysinit //緊接着,運行系統第一個腳本/etc/rc.d/rc/sysinit
- //它的action:sysyinit指的是定義系統初始化過程
- l0:0:wait:/etc/rc.d/rc 0
- l1:1:wait:/etc/rc.d/rc 1
- l2:2:wait:/etc/rc.d/rc 2 //然后就是加載服務了,他們被定義在/etc/rc.d/rcN.d
- l3:3:wait:/etc/rc.d/rc 3 //action:waite 這個進程在在對應級別啟動一次,直到它結束為止,我的系統啟動級別為3,所有執行rc 3對應的服務
- l4:4:wait:/etc/rc.d/rc 4
- l5:5:wait:/etc/rc.d/rc 5
- l6:6:wait:/etc/rc.d/rc 6
- ca::ctrlaltdel:/sbin/shutdown -t3 -r now //這里定義了一個組合快捷鍵,熟悉吧,沒錯就是重啟,你可以把它注釋掉不用
- pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down" //這里定義了ups電源,powerfail 指的是如果突然斷電,它對應的process命令是,提示用戶系統電源失效,將要關機,提醒用戶把數據都存儲好
- pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled" //這里的action,powerokwaite,指的是系統恢復供電,關機取消...
- 1:2345:respawn:/sbin/mingetty tty1 //開啟終端,在系統准備工作做好后,就會啟動出6個終端,tty1~6 mingetyy就是終端的執行命令
- 2:2345:respawn:/sbin/mingetty tty2 //可以看到他們對應的級別是2345,你也可以注釋掉幾個,這樣啟動后,就會開啟你指定個數的終端...
- 3:2345:respawn:/sbin/mingetty tty3 //這里的動作respawn意思是如果用戶關閉,那么這個進程會立刻再次執行
- 4:2345:respawn:/sbin/mingetty tty4
- 5:2345:respawn:/sbin/mingetty tty5
- 6:2345:respawn:/sbin/mingetty tty6
- x:5:respawn:/etc/X11/prefdm -nodaemon //當一切准備完畢,就會運行守護進程,它是不分運行級別的,同樣也是respawn
2. 關於etc/rc.d/rc.sysinit 和 /etc/rc.d/rc.Nd
在inittab文件中,我們了解到rc.sysinit是系統運行的第一個腳本,那么它的作用都有哪些呢?如果有shell腳本基礎的話可以用vim打開這個文件來看看,它有900多行...是linux里最牛的腳本,總結下它的作用,有如下幾點:(一定要牢記,必要時我們要重寫它!)
1. 激活udev和selinux
2. 通過讀取/etc/sysct1.conf來設置內核參數
3. 設置系統時鍾
4. 設置鍵盤映射
5. 啟動交換分區。
6. 設置主機名
7. 檢查根文件系統,如果沒有錯誤,以讀寫的方式來重新掛載文件系統 (重要)
8. 激活raid和lvm設備
9. 啟動磁盤限額
10 掛載其他/etc/fstab里定義的尚未被掛載的設備
11. 清理過時的文件鎖和PID文件
12. 啟動服務/etc/rc.d/rcN.d
/etc/rc.d/rcN.d 是一個存放了系統啟動是加載的服務的文件夾,通過inittab,我們知道了,它的編號對應相應的啟動運行級別。
需要說明的是,它里面的文件,以我的為例 以K開頭的代表stop,以S開頭的代表啟動start,數字越小,有限級別越高
- [root@server69 rc.d]# cd rc3.d
- [root@server69 rc3.d]# ls
- K01dnsmasq K74nscd S08ip6tables S25bluetooth S85gpm
- K02avahi-dnsconfd K85mdmpd S08iptables S25netfs S90crond
- K02NetworkManager K87multipathd S08mcstrans S25pcscd S90xfs
- K05conman K88wpa_supplicant S10network S26acpid S95anacron
- K05saslauthd K89dund S11auditd S26apmd S95atd
- K10psacct K89netplugd S12restorecond S26haldaemon S97rhnsd
- K20nfs K89pand S12syslog S26hidd S97yum-updatesd
- K24irda K89rdisc S13irqbalance S28autofs S98avahi-daemon
- K35vncserver K99readahead_later S13portmap S50hplip S99firstboot
- K35winbind S00microcode_ctl S14nfslock S55sshd S99local
- K50ibmasm S02lvm2-monitor S15mdmonitor S56cups S99smartd
- K50netconsole S04readahead_early S18rpcidmapd S56rawdevices
- K69rpcsvcgssd S05kudzu S19rpcgssd S58ntpd
- K73ypbind S06cpuspeed S22messagebus S80sendmail
3./etc/fstab文件
它定義了系統初始化掛載的設備,對系統的啟動至關重要,rc.sysinit通過讀取它來實現系統分區的掛載
它的內容如何呢?我們一起來看下
要掛載的設備 掛載點 文件系統類型 掛載選項 轉儲頻率 文件自檢次序
- /dev/sda2 / ext3 defaults 0 0
- /dev/sda1 /boot ext3 defaults 0 0
- sysfs /sys sysfs defaults 0 0
- proc /proc proc defaults 0 0
我截取了4個系統啟動時必須掛載的設備
/ 根文件目錄,由它你才可以進入linux的世界,它在/dev/sda2里
/boot 啟動目錄,在/下,里面有grub,initrd和系統內核,它在/dev/sda1
sysfs 一個虛擬的文件系統,產生包含所有硬件層次視圖,和/proc類似
proc 這是一個虛擬的目錄,它映射內存里的信息對應進程信息,也就是說它對應的是內存而不是硬盤
好了,了解了這些文件和目錄的作用,我們在回過頭來看那張流程圖:
1. linux開機從POST加電自檢開始,當自檢完成,讀取第一個硬盤的第0個磁頭里的前446個字節,運行里面的bootloader,linux一般 用的是grub。
2. 通過grub傳遞參數給內核,初始化加載內核過程,內核調用initrd(小型內存文件系統,五臟俱全,是一個微型linux),通過 initrd,以只讀方式掛載根文件系統
3. 當根文件系統被掛載后,就會讀取並運行/sbin/init來進行初始化工作。
4. 按次序依次執行/rc/sysinit ,這個時候會重新以讀寫的方式掛載根文件系統
5. 讀取/etc/rc.d/rcN.d/來啟動以s開頭的服務,停止以k開頭的服務
6. 當一切准備完畢,打印終端,出現熟悉的Login界面!(當然,如果你是以5級別啟動的話,Linux就會啟動圖形界面~)
運行一個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