在日常使用電腦的時候大家可能會遇到這樣一種場景,計算機昨天用的好好的,下班關機走人,結果第二天一開機發現起不來了,這是爲何?接下來就聊聊系統啓動的那些事。
因爲是linux與windows在引導系統所用的工具不同(WINDOWS用的ntloader),接下來主要以Linux爲主嘮叨嘮叨(老年癡呆提前了,沒轍)。
計算機的開機按電源鍵接通電源應該算是第一步吧(專業小白三十年,一直被模仿從未被超越),加電以後開始通過BIOS中的設置來先進行硬件的一些自檢,其中包括了CPU這傢伙在不在啊,他要是在能不能正常工作啊,要是生病了得囑咐他多喝熱水啊(這句話是跟你女朋友說的,什麼!沒有女票,那就跟別人女朋友說吧,捱打可別來找我O(∩_∩)O~)。
所有硬件檢測都沒問題了,接下來就會根據bios中的boot sequence設置值來開始查找第一個有引導程序的設備,這個其實就是選定的設備中有沒有bootloader這個程序可以幫助我們來啓動系統的,下面就進入介紹boot loader這個得力干將階段。
Boot loader中文名叫引導加載器,他的發展史比較早,初期分成了兩個階段,第一個階段是稱爲LILO(Linux loader)這個因爲早期的硬件設備都比較簡單,他也就隨大流簡簡單單了起來,雖說簡單但麻雀雖小五臟俱全,該有的都有了只是因爲硬件發展的太快了導致他顯得力不從心逐漸的被第二階段的grub程序所取代。萬萬沒想到,智能手機大爆發,LILO迎來了他的第二春~,此處不表,自己看去吧。
Grub(Grand Uniform BootLoader)的發展從廣義上來說也分爲兩個階段,即0.X階段被稱爲GRUB Legacy,在CentOS7後發展到了第二階段即1.X,被稱爲GRUB2。
究竟Boot loader有什麼作用呢?一個作用就是我們啓動計算機即將進入centos系統時看到的那個交互式的選擇菜單;另一個作用就是可以讓用戶選擇的系統加載到內存中啓動,當系統安裝完成之時,系統的內核文件一般會放置在單獨劃分的boot分區當中,隨之就帶來了一個問題,內核放置在硬盤當中,要讀取內核文件就要先讀取硬盤驅動,然後讀取文件系統最後纔會讀取到內核文件,而驅動程序是放置在硬盤當中,此時就陷入了一個“怪圈”,怎麼解決呢?grub程序提出了“三段式”解決方案。
第一階段被稱爲stage1,在這一階段是運行boot loader程序,bootloader程序在MBR中,(MBR空間只有512bytes,bootloader佔用了446bytes還有64bytes用來記錄硬盤分區表,2bytes來記錄bootloader程序是否有效),此時運行的boot loader程序只是最基本的功能,一些配置文件則被放置在stage2中。
第二階段爲stage1_5,他被存放在MBR隨後的扇區中,存放了可以讓boot loader能成功進入stage2階段的文件系統的驅動。
第三階段爲stage2,在這個階段boot loader開始加載所有的配置文件和一些系統啓動的環境參數,這些所需的文件被存儲在磁盤/boot分區中,第三階段運行完畢一切正常的情況下,內核即被加載到內存中,引導階段算是完成了,剩餘的工作就移交給內核。
內核開始進行自解壓,展開做自身初始化,探測各硬件設備並裝載設備的驅動程序,然後以只讀方式掛載根文件系統,最後一步則是運行用戶空間的第一個程序init,後續的系統初始化工作都移交到init來操作。
而init服務會根據其配置文件/etc/rc.d/rc.sysinit來進行系統初始化,它所設置的內容有以下幾點:
1、設置主機名
2、設置歡迎信息
3、激活udev和selinux
4、掛載/etc/fstab文件中定義的文件系統
5、檢測根文件系統,並以讀寫方式重新掛載根文件系統
6、設置系統時鐘
7、激活swap交換分區設備
8、根據/etc/sysctl.conf文件設置內核參數
9、激活Lvm及software raid設備
10、加載額外設備例如打印機等的驅動程序
11、清理操作
下面做一些腳本練習
===我是分割線===**===我是分割線===**===我是分割線===**===我是分割線===
1、寫一個腳本,完成如下功能
(1) 傳遞一個磁盤設備文件路徑給腳本,判斷設備是否存在;
(2) 如果存在,則顯示此設備上的所有分區信息;
#!/bin/bash # #Program:View disk space information #History:0.0.1 2017/03/07 22:06 #Author: MG disk='' dir="/tmp/" until [ $disk ];do echo "example:/dev/sd[a-z]" read -p "input a disk which you want to view: " disk done if `fdisk -l $disk >${dir}/success 2>${dir}/error` ;then cat ${dir}/success else cat ${dir}/error fi
2、寫一個腳本,完成如下功能
傳遞一個參數給腳本,此參數爲gzip、bzip2或者xz三者之一;
(1) 如果參數1的值爲gzip,則使用tar和gzip歸檔壓縮/etc目錄至/backups目錄中,並命名爲/backups/etc-20160613.tar.gz;
(2) 如果參數1的值爲bzip2,則使用tar和bzip2歸檔壓縮/etc目錄至/backups目錄中,並命名爲/backups/etc-20160613.tar.bz2;
(3) 如果參數1的值爲xz,使用tar和xz歸檔壓縮/etc目錄至/backups目錄中,並命名爲/backups/etc-20160613.tar.xz;
(4) 其他任意值,則顯示錯誤壓縮工具,並執行非正常退出;
#!/bin/bash # #Program:Three packaging methods #History:0.0.1 2017/03/07 22:36 #Author: MG style=("gzip" "bzip2" "xz") choice='' sour=/etc [ -d /backups ] || mkdir /backups file=/backups/etc-20160603.tar. function options (){ echo "compress arguments:" echo "==========================" for (( i=0;i<${#style[@]};i++)) ;do echo -e "${style[$i]}\033[6G)\033[0m" done echo "==========================" } options until [ $choice ];do read -p "select an option which you want to compress: " choice done case $choice in gzip) tar -zcf ${file}gz $sour &>/dev/null ;; bzip2) tar -jcf ${file}bz2 $sour &>/dev/null ;; xz) tar -Jcf ${file}xz $sour &>/dev/null ;; *) echo "wrong argument,script exit" exit 10 #wrong argument ;; esac
3、 寫一個腳本,接受一個路徑參數;
(1) 如果爲普通文件,則說明其可被正常訪問;
(2) 如果是目錄文件,則說明是個訪問路徑;
(3) 如果爲符號鏈接文件,則說明是個訪問路徑;
(4) 其他爲無法判斷;
#!/bin/bash # #Program:Determine file type #History:0.0.1 2017/03/08 09:45 #Author: MG echo "example /tmp/test" read -p "Please input a file: " File if [ ! -z $File ];then #flag="true" && echo $flag if [ -d $File ];then echo "${File} is a directory." elif [ -f $File ];then echo "${File} is a common file." elif [ -l $File ];then echo "${File} is a symlink file." else echo "unknown file type ." fi else #flag="false" &&echo $flag echo "wrong arguments" exit 100 #wrong argument fi
4、 寫一個腳本,取得當前主機的主機名,判斷
(1) 如果主機名爲空或爲localhost,或爲"(none)",則將其命名爲mail.mageedu.com;
(2) 否則,顯示現有的主機名即可;
#!/bin/bash # #Program:show the hostname for CentOS7 #History:0.0.1 2017/03/08 08:43 #Author: MG HNAME=`/bin/hostname` if [ -z $HNAME -o $HNAME == "(none)" -o $HNAME == 'localhost' ];then hostnamectl set-hostname "mail.mageedu.com" else echo $HNAME fi
5、 寫一個腳本,完成如下任務
(1) 按順序分別複製/var/log目錄下的每個直接文件或子目錄至/tmp/test1-testn目錄中;
(2) 複製目錄時,才使用cp -r 命令;
(3) 複製文件時使用cp命令;
(4) 複製鏈接文件時使用cp -d命令;
(5) 餘下的所有類型,使用cp -a 命令;
#!/bin/bash # #Program:Determine file type #History:0.0.1 2017/03/08 09:45 #Author: MG src=/var/log dsrc=/tmp/test1-testn [ -d $dsrc ] || mkdir $dsrc echo "ready to copy file wait a moment please." for i in ${src}/* ;do if [ -d $i ];then cp -r $i ${dsrc}/ elif [ -f $i ];then cp -r $i ${dsrc}/ elif [ -l $i ];then cp -d $i ${dsrc}/ else cp -a $i ${dsrc}/ fi done echo -e "Operation completed!"
6、 請詳細描述CentOS系統的啓動流程(詳細到每個過程系統做了哪些事情)
見文章開篇介紹
7、爲運行於虛擬機上的CentOS6添加一塊新硬盤,提供兩個主分區;
(1) 爲硬盤新建兩個主分區;併爲其安裝grub;
(2) 爲硬盤的第一個主分區提供內核和ramdisk文件;爲第二個分區提供rootfs;
(3) 爲rootfs提供bash、ls、cat程序及所依賴的庫文件;
(4) 爲grub提供配置文件;
(5) 將新的硬盤設置爲第一啓動項並能夠正常啓動目標主機;
[root@localhost ~]# fdisk -l Disk /dev/sda: 128.8 GB, 128849018880 bytes 255 heads, 63 sectors/track, 15665 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x0005d62f Device Boot Start End Blocks Id System /dev/sda1 * 1 64 512000 83 Linux Partition 1 does not end on cylinder boundary. /dev/sda2 64 1339 10240000 83 Linux /dev/sda3 1339 1594 2048000 82 Linux swap / Solaris /dev/sda4 1594 15666 113028096 5 Extended /dev/sda5 1594 4144 20480000 83 Linux /dev/sda6 4144 5419 10240000 83 Linux Disk /dev/sdb: 21.5 GB, 21474836480 bytes 255 heads, 63 sectors/track, 2610 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x00000000
[root@localhost ~]# partx -a /dev/sdb BLKPG: Device or resource busy error adding partition 1 BLKPG: Device or resource busy error adding partition 2 BLKPG: Device or resource busy error adding partition 3
[root@localhost ~]# cat /proc/partitions major minor #blocks name 8 0 125829120 sda 8 1 512000 sda1 8 2 10240000 sda2 8 3 2048000 sda3 8 4 1 sda4 8 5 20480000 sda5 8 6 10240000 sda6 8 16 20971520 sdb 8 17 112423 sdb1 8 18 2104515 sdb2 8 19 10490445 sdb3
[root@localhost ~]# mkfs -t ext4 -L "grubboot" /dev/sdb1 ...格式化數據省略
[root@localhost ~]# mkswap /dev/sdb2 Setting up swapspace version 1, size = 2104508 KiB no label, UUID=b3a5110c-34f5-459a-aa04-48670d8061ca
[root@localhost ~]# mkfs -t ext4 -L "grubrootfs" /dev/sdb3 mke2fs 1.41.12 (17-May-2010) 文件系統標籤=grubrootfs 操作系統:Linux 塊大小=4096 (log=2) 分塊大小=4096 (log=2) ...省略後續數據
[root@localhost ~]# mkdir /mnt/boot #給sdb安裝grub時,grub-install命令必須要指定一個boot目錄,否則無法完成操作 [root@localhost ~]# mount /dev/sdb1 /mnt/boot #將創建的sdb1分區掛載至boot目錄
[root@localhost ~]# grub-install --root-directory=/mnt /dev/sdb #root-directory是指定grub的根,後邊的/dev/sdb指定的則是爲哪個硬盤安裝grub Probing devices to guess BIOS drives. This may take a long time. Installation finished. No error reported. This is the contents of the device map /mnt/boot/grub/device.map. Check if this is correct or not. If any of the lines is incorrect, fix it and re-run the script `grub-install'. (fd0)/dev/fd0 (hd0)/dev/sda (hd1)/dev/sdb
#此時grub程序已經安裝完成,但要想使其可以單獨掛載至獨立主機上並能成功運行則還需要有內核文件以及所需要的initramdisk,下面將系統的這兩個文件複製到/mnt/boot目錄中 [root@localhost mnt]# cp /boot/vmlinuz-2.6.32-573.el6.x86_64 /mnt/boot/vmlinuz-2.6.32 [root@localhost mnt]# cp /boot/initramfs-2.6.32-573.el6.x86_64.img /mnt/boot/initrd-2.6.32
#當然系統啓動時還需要一個grub的配置文件,來設置啓動菜單以及啓動時相關參數,可以複製本機的grub.conf也可以自己手動創建一個配置文件 [root@localhost mnt]# vim boot/grub/grub.conf default=0 timeout=3 hiddenmenu title Centos6.7 (grub installation test) root (hd0,0) kernel /vmlinuz-2.6.32 ro root=/dev/sda3 init=/bin/bash #此版本僅做測試使用,所以系統初始化完成之後啓動的第一個用戶空間進程不是/sbin/init而是/bin/bash。 initrd /initramfs-2.6.32 #保存退出,grub配置文件完成
現在就差最後一步了,系統初始化完成之後還需要一個啓動終端來跟用戶交互,但目前這個簡易系統是沒有任何工具可用的,現在就來寫一個腳本來將當前系統的ls、cat和bash這三個工具複製到簡易系統當中來,現在先將系統用到的一些目錄創建出來。(腳本參照12題)
[root@localhost mnt]# cd sysroot [root@localhost sysroot]# mkdir -pv bin sbin lib lib64 home root etc dev sys proc var usr mnt media
8、 寫一個腳本
(1) 能夠接受四個參數:start,stop,restart,status
start:輸出"starting 腳本名finished."
stop:輸出"stop 腳本名stopped."
restart:輸出"stopping 腳本名 starting 腳本名 finished."
status:輸出"腳本名當前狀態"
(2) 其他任意參數,均報錯退出;
#!/bin/bash # #Program:progscript.sh #History:0.0.1 2017/03/08 21:09 #Author: MG cat <<EOF script argument ====================================== 1. start ) start the program || 2. stop ) stop the program || 3. restart ) restart the program || 4. status ) show the program status || ====================================== EOF declare -i flag=0 read -p "Select the parameters to be operated: " ARGU START="Starting the ${0} ." STOP="Stopping the ${0} ." case $ARGU in 1|start) echo "$START" flag=0 ;; 2|stop) echo "$STOP" flag=1 ;; 3|restart) if [ $flag -eq 0 ];then echo "$STOP" && echo "$START" flag=0 else echo "$START" flag=0 fi ;; 4|status) if [ $flag -eq 0 ];then echo "${0} has started." else echo "${0} is not running." fi ;; *) echo "You select the wrong option,${0} exit!" exit 12 #wrong option ;; esac
9、寫一個腳本,判斷給定的用戶是否登錄了當前系統
(1) 如果登錄了,則顯示用戶登錄,腳本終止;
(2) 每3秒鐘,查看一個用戶是否登錄;
#!/bin/bash # #Program:loginuser.sh #History:0.0.1 2017/03/08 21:54 #Author: MG until `who |awk '{print $1}'|grep $1 &>/dev/null`;do sleep 3 done echo "$1 has login."
10、寫一個腳本,顯示用戶選定要查看的信息
cpu) display cpu info
mem) display memory info
disk) display disk info
quit) quit
非此四項選擇,則提示錯誤,並要求用戶重新選擇,直到其給出正確選擇爲止;
#!/bin/bash # #Program:serverinfo.sh #History:0.0.1 2017/03/08 22:03 #Author: MG choice='null' until [ $choice == 'cpu' -o $choice == 'mem' -o $choice == 'disk' -o $choice == 'quit' ];do cat <<EOF cpu) display cpu info mem) display memory info disk) display disk info quit) quit EOF read -p "Input an option: " choice choice=${choice:='null'} done case $choice in cpu) cat /proc/cpuinfo ;; mem) free -mh ;; disk) fdisk -l ;; quit) echo "exit the script." ;; esac
11、 寫一個腳本
(1) 用函數實現返回一個用戶的UID和SHELL;用戶通過參數傳遞而來
(2) 提示用戶輸入一個用戶名或輸入"quit" 退出;
當輸入的是用戶名,則調用函數顯示用戶信息;
當用戶輸入quit,則退出腳本;進一步顯示鍵入的用戶相關信息後,再次提醒輸出用戶名或quit;
#!/bin/bash # #Program:get user ID and shell #History:0.0.1 2017/03/08 23:19 #Author: MG choice='null' until [ $choice == 'quit' ];do echo "input quit could exit this program." read -p "Input one user name: " choice choice=${choice:=null} if [ $choice != 'quit' -a $choice != 'null' ];then id $choice &>/dev/null if [ $? -eq 0 ];then cat /etc/passwd |grep $choice |awk -v FS=: -v OFS=: '{print $1,$3,$6}' fi fi done echo "quit!"
12、寫一個腳本,完成如下功能(使用函數)
(1) 提示用戶輸入一個可執行命令的名字;獲取此命令依賴的所有庫文件;
(2) 複製命令文件至/mnt/sysroot目錄下的對應rootfs的路徑上,例如,如果複製的文件原路徑是/usr/bin/useradd,則複製到/mnt/sysroot/usr/bin/目錄中;
(3) 複製此命令依賴的各庫文件至/mnt/sysroot目錄下的對應的rootfs的路徑上;規則同上面命令相關的要求;
#!/bin/bash # #Program:use this program to backup the system command #History:0.0.1 2017/03/08 23:19 #Author: MG declare -i DebugLevel=0 RED="\033[1;31m" GREEN="\033[32m" ORANGE="\033[36m" COL="\033[60G" FLICKER="\033[5m" END="\033[0m" SUCCEED="${COL}[ ${GREEN}succeed${END} ]" ERROR="${COL}[ ${RED}failed${END} ]" Target=/mnt/sysroot function checkQ(){ if [ -n "$1" ];then if [ $1 == 'q' -o $1 == 'Q' ];then echo "Exit the script!" #break 3 exit fi fi } function inputCOM(){ flag=$1 flag=${flag:=newCOM} case $flag in "newCOM") unset Command until [ -n "$Command" ];do read -p "Input a command: " Command done ;; "renewCOM") if [ -z "$Command" ];then until [ -n "$Command" ];do read -p "Input a correct command: " Command done else read -p "Input a correct command: " Command until [ -n "$Command" ];do read -p "Input a correct command: " Command done fi ;; esac } [ -d $Target ] || mkdir -p $Target #ll $Target inputCOM while [ $Command != 'q' -a $Command != 'Q' ];do `which $Command &>/dev/null` Flag=$? #echo "Flag=$?" until [ $Flag -eq 0 ];do echo -e "Wrong command ${RED}$Command${END}" inputCOM "renewCOM" `which $Command &>/dev/null` Flag=$? checkQ $Command done Command=`which $Command|grep -v "^alias.*"|grep -o "[^[:space:]].*"` Comdir=${Command%/*} [ ! -d ${Target}${Comdir} ] && mkdir -p ${Target}${Comdir} && echo -e "${Target}${Comdir} create ${COL}[ ${GREEN}succeed${END} ]${END}" [ ! -f ${Target}${Command} ] && cp $Command ${Target}${Command} && echo -e "${Command} has copied $Target ${COL}[ ${GREEN}succeed${END} ]" [ $DebugLevel -eq 1 ] && echo $Comdir [ $DebugLevel -eq 1 ] && echo $Command for Lib in `ldd $Command |grep -o "[^[:space:]]*/lib.[^[:space:]]*"`;do [ $DebugLevel -eq 1 ] && echo $Lib LibDir=${Lib%/*} [ $DebugLevel -eq 1 ] && echo $LibDir [ ! -d ${Target}${LibDir} ] && mkdir -p ${Target}${LibDir} && echo -e "${Target}${LibDir} create $SUCCEED" [ ! -f ${Target}${Lib} ] && cp $Lib ${Target}${Lib} && echo -e "$Lib has copied ${Target} $SUCCEED" done echo -e "${GREEN}Operation has been completed!${END}${COL}[ ${GREEN}${FLICKER}OK${END} ]" inputCOM done