1. busybox簡介
busybox是一個集成了一百多個最常用linux命令和工具的軟件,它將許多常用的LINUX命令和工具結合到了一個單獨的可執行程序中。雖然與相應的GNU工具比較起來,busybox所提供的功能和參數略少,但在比較小的系統(例如啓動盤)或者嵌入式系統中,已經足夠了。
busybox在設計上就充分考慮了硬件資源受限的特殊工作環境。它採用一種很巧妙的辦法減少自己的體積:所有的命令都通過“插件”的方式集中到一個可執行文件中,在實際應用過程中通過不同的符號鏈接來確定到底要執行哪個操作。例如最終生成的可執行文件爲busybox,當爲它建立一個符號鏈接ls的時候,就可以通過執行這個新命令實現列目錄的功能。採用單一執行文件的方式最大限度地共享了程序代碼,甚至連文件頭、內存中的程序控制塊等其他操作系統資源都共享了,對於資源比較緊張的系統來說,是最合適不過了。
Busybox配置如下:
Build Options---> [*] Build BusyBox as a static binary (no shared libs)
Installation Options ---> Do you want to build BusyBox with a Cross Compiler。如果要對其他平臺進行編譯就要選擇它並設置相應的編譯程序前綴。我們選擇armv5l-linux-,前面加上絕對路徑。 Login/Password Management Utilities---> [*]Use internal password and group functions rather than system functions。這裏設置使用busybox自己的password和shadow文件的功能。 如果需要一個交互的登錄界面,則選擇getty、login和passwd |
編譯make TARGET_ARCH=arm,生成的目標代碼位於_install目錄下。
2. 文件系統啓動過程
Linux的啓動過程主要分成兩個階段:
1.啓動內核。在這個階段,內核裝入內存並在初始化每個設備驅動器時打印信息。
2.執行程序init。裝入內核並初始化設備後,運行init程序。init程序處理所有程序的啓動,包括重要系統程序和其它指定在啓動時裝入的軟件。
現在主要詳細介紹一下文件系統的啓動過程,即linux啓動過程的第二階段,大概分爲以下幾個過程:
(1)運行init
init的進程號是1,從這一點就能看出,init進程是系統所有進程的起點,linux在完成核內引導以後,就開始運行init程序。init程序需要讀取配置文件/etc/inittab,以查看下一步做什麼。inittab是一個不可執行的文本文件,它有若干行指令所組成,告訴 init 要進入什麼運行級別,以及在哪裏可以找到該運行級別的配置文件。以下是qsan的inittab文件(部分註釋省略):
# The default runlevel. id:4:initdefault: # Boot-time system configuration/initialization script. # This is run first except when booting in emergency (-b) mode. si::sysinit:/etc/init.d/rcS # /etc/init.d executes the S and K scripts upon change of runlevel. l0:0:wait:/etc/init.d/rc 0 l1:1:wait:/etc/init.d/rc 1 l2:2:wait:/etc/init.d/rc 2 l3:3:wait:/etc/init.d/rc 3 l4:4:wait:/etc/init.d/rc 4 l6:6:wait:/etc/init.d/rc 6 # Trap CTRL-ALT-DELETE ca::ctrlaltdel:/sbin/shutdown -t3 -h now # /sbin/getty invocations for the runlevels. # Example how to put a getty on a serial line (for a terminal) T0:134:respawn:/sbin/getty -L ttyS0 115200 vt100 T1:1:respawn:/sbin/getty -L ttyS1 115200 vt100 |
以上面的inittab文件爲例,來說明一下inittab的格式。其中以#開始的行是註釋行,除了註釋行之外,每一行都有以下格式:
id:runlevel:action:process
id – 入口標識符,用於標識文件/etc/inittab中的每一個登記項。它是一個1-4位的字符串,對於getty或mingetty等其他login程序項,要求id與tty的編號相同,否則getty程序將不能正常工作。
runlevel – 運行級。說明該登記項適用於哪一個運行級。爲空表示適用於所有級別.它是init所處於的運行級別標識,一般使用0-6以及S或s。0、1、6運行級別被系統保留。0作爲halt動作,1作爲重啓至單用戶模式,6爲重啓。S和s意義相同,表示單用戶模式,且無需inittab文件,因此也不在inittab中出現,實際上,進入單用戶模式時,init直接在控制檯(/dev/console)上運行/sbin/sulogin。runlevel可以是並列的多個值,以匹配多個運行級別,對大多數action來說,僅當runlevel與當前運行級別匹配成功纔會執行。
action – 定義init命令應該向進程實施什麼動作。包括以下:
respawn-無論何時它終止,均重新啓動命令
wait-運行命令一次。在繼續之前,init等待它終止
once-運行命令一次
boot-命令在啓動過程中運行。忽略運行等級字段
bootwait-命令在啓動過程中運行,忽略運行等級字段。在繼續之前,init等待該進程終止
initdefault-定義Linux系統的默認運行等級
powerwait-停電時命令運行。在繼續之前,init等待該進程終止
powerfail-停電時命令運行。在繼續之前,init不等待該進程終止
powerokwait-恢復電力時命令運行。在繼續之前,init等待該進程終止
powerfailnow-UPS發出電池即將耗盡的信號時,運行該命令
process - 是具體的執行程序。程序後面可以帶參數。
(2)系統初始化
sysinit、boot、bootwait等action將在系統啓動時無條件運行,而忽略其中的runlevel。因此init進程首先會執行etc/init.d/rcS腳本,rcS內容如下:
#首先,定義PATH、runlevel、prevlevel然後導出到環境中 PATH=/sbin:/bin:/usr/sbin:/usr/bin runlevel=S prevlevel=N umask 022 export PATH runlevel prevlevel #然後,判斷是不是第一次安裝系統,如果是,則檢查並且執行安裝程序留下的腳本 if [ -x /sbin/unconfigured.sh ] then /sbin/unconfigured.sh fi
. /etc/default/rcS export VERBOSE
#捕捉INT、QUIT、TSTP信號, trap":"INT QUIT TSTP
#檢查/etc/rcS.d/目錄,看是否有以S開頭並且緊跟兩個字符(實際上 #一般是兩個數字0-99)命名的非普通(! -f"$i")文件,如果有則根據 #文件的類型作出兩個選擇 # 1,是.sh結尾的腳本時執行 # 2,如果不是.sh結尾的腳本,則傳遞給start參數執行這個文件 for i in /etc/rcS.d/S??* do # Ignore dangling symlinks for now. [ ! -f"$i"]&& continue
case"$i"in *.sh) # Source shell script for speed. ( trap - INT QUIT TSTP set start . $i ) ;; *) # No sh extension, so fork subprocess. $i start ;; esac done
#這是爲了兼容其他系統的/etc/rc.boot腳本 [ -d /etc/rc.boot ]&& run-parts /etc/rc.boot
#這也是用於第一次安裝系統後需要執行的腳本,安裝成功後,系統上 #一般沒有這個腳本 if [ -x /sbin/setup.sh ] then /sbin/setup.sh fi
#/etc/rc.S/rcS腳本執行結束.返回/inittab |
(3)啓動對應運行級別的守護進程
返回/inittab,接下來根據系統進入的運行級別,啓動對應運行級別的守護進程,這裏爲4,init將執行配置文件inittab中的以下這行:
l4:4:wait:/etc/init.d/rc 4
這一行表示以4爲參數運行/etc/rc.d/rc,/etc/rc.d/rc是一個Shell腳本,它接受4作爲參數,去執行/etc/rc.d/rc4.d/目錄下的所有的rc啓動腳本,/etc/rc.d/rc4.d/目錄中的這些啓動腳本實際上都是一些鏈接文件,而不是真正的rc啓動腳本,真正的rc啓動腳本實際上都是放在/etc/rc.d/init.d/目錄下。而這些rc啓動腳本有着類似的用法,它們一般能接受start、stop、restart、status等參數。
/etc/rc.d/rc4.d/中的rc啓動腳本通常是K或S開頭的鏈接文件,對於以S開頭的啓動腳本,將以start參數來運行。而如果發現存在相應的腳本也存在K打頭的鏈接,而且已經處於運行態了(以/var/lock/subsys/下的文件作爲標誌),則將首先以stop爲參數停止這些已經啓動了的守護進程,然後再重新運行。這樣做是爲了保證是當init改變運行級別時,所有相關的守護進程都將重啓。
(4)建立終端
rc執行完畢後,返回init。這時基本系統環境已經設置好了,各種守護進程也已經啓動了。init接下來會打開終端,以便用戶登錄系統,如以下2行:
T0:134:respawn:/sbin/getty -L ttyS0 115200 vt100
T1:1:respawn:/sbin/getty -L ttyS1 115200 vt100
從上面可以看出在1、3、4的運行級別中將以respawn方式運行getty程序,它會顯示一個文本登錄界面,這個界面就是我們經常看到的登錄界面,在這個登錄界面中會提示用戶輸入用戶名,而用戶輸入的用戶名將作爲參數傳給login程序來驗證用戶的身份。
注意:如果想繞過登錄驗證過程,想直接進入shell界面的話,則把以上兩行註釋掉,改爲:T0:134:respawn:/bin/sh
(5)登錄系統,啓動完成
getty進程接收到用戶名後,啓動login進程.
login進程要求用戶輸入口令.
用戶輸入口令.
login進程對username和password進行檢查.
login啓動shell進程.
shell進程根據/etc/password中的shell類型,啓動相應的shell.並啓動/etc/profile文件和$HOME/.bash_profile文件.最後出現shell提示符,等待用戶輸入命令.
至此,啓動過程結束。
3. login驗證過程
Linux的帳號驗證程序是login,login會接收getty傳來的用戶名作爲用戶名參數。然後login會對用戶名進行分析:如果用戶名不是root,且存在/etc/nologin文件,login將輸出nologin文件的內容,然後退出。這通常用來系統維護時防止非root用戶登錄。只有/etc/securetty中登記了的終端才允許root用戶登錄,如果不存在這個文件,則root可以在任何終端上登錄。/etc/usertty文件用於對用戶作出附加訪問限制,如果不存在這個文件,則沒有其他限制。
在分析完用戶名後,login將搜索/etc/passwd以及/etc/shadow來驗證密碼以及設置帳戶的其它信息,比如:主目錄是什麼、使用何種shell。如果沒有指定主目錄,將默認爲根目錄;如果沒有指定shell,將默認爲/bin/bash。
login程序成功後,會向對應的終端在輸出最近一次登錄的信息(在/var/log/lastlog中有記錄),並檢查用戶是否有新郵件(在/usr/spool/mail/的對應用戶名目錄下)。然後開始設置各種環境變量:對於bash來說,系統首先尋找/etc/profile腳本文件,並執行它;然後如果用戶的主目錄中存在.bash_profile文件,就執行它,在這些文件中又可能調用了其它配置文件,所有的配置文件執行後,各種環境變量也設好了,這時會出現大家熟悉的命令行提示符,到此整個啓動過程就結束了。
以下是passwd,shadow和group腳本的格式說明:
/etc/passwd密碼文件的格式如下所示: 用戶名:口令:用戶標識號:組標識號:註釋性描述:主目錄:登錄Shell user_name:password:uid:gid:comment:home:shell 每行有很多項組成,項與項之間用":"隔開.每項的說明如下: user_name 用戶名 password 登錄密碼,初始設置時爲空 uid 用戶識別號(User ID),是一數值,每個用戶的識別號不同 gid 用戶組識別號,參見/etc/group文件 comment 註釋,可以任意字符,一般用來說明用戶的身份特徵 home 家目錄名 shell 該用戶缺省shell,一般取值爲:/bin/sh,/bin/ksh,/bin/csh /etc/shadow文件格式如下: 登錄名:加密口令:最後一次修改時間:最小時間間隔:最大時間間隔:警告時間:不活動時間:失效時間:標誌 username:passwd:last:may:must:warn:expire:disable:reserved username 使用者名稱 passwd 編碼密碼 last 密碼上次更動日期,以從1970年1月1日算起的天數代表 may 密碼改變前天數 must 密碼最常使用天數 warn 代表期限前幾天就事先警告使用者 expire 超過密碼過期天數後,就關閉該帳號 disable 帳號關閉,以從1970年1月1日算起的天數代表 reserved 預備欄位 /etc/group文件格式如下: group_name:password:gid:members_list 每行有四項組成,項與項之間用":"隔開. group_name 用戶組名 password 用戶組密碼,一般爲空 gid 用戶組識別號(Group ID),是一數值,每個組的識別號不同 members_list 該組成員列表,由一個或多個用戶名組成,用戶名之間用逗號隔開 |
注:一個最簡單的文件系統至少需要包含以下幾個目錄,/sbin,/bin,/dev(需要console和ttyS0兩個文件),/proc,/etc(需要inittab,rcS文件),/home.
嵌入式linux用戶密碼驗證
開發板啓動內核後,一般是直接按回車鍵就能直接進入文件系統,以前一直搞不懂爲什麼不需要用戶密碼
驗證,今天上網查看了一下,原來是因爲配置文件的問題。
原來一直以爲用戶密碼驗證是由運用程序做的,後來,上網查了一下,原來是由文件系統完成的,
在編譯busybox的時候把相關的選項選上,就可以支持用戶密碼登錄了,見下圖。
編譯好busybox之後,在可執行文件中,可以找一個文件名爲login的軟鏈接,就是負責用戶驗證登錄的。
可以直接拷貝主機的passwd 和 shadow 兩個文件到開發板的/etc目錄下.
在/etc/init.d/rcS文件中加入
/bin/login
重啓開發板,登錄就需要密碼驗證了
還可以在/etc創建一個inittab文件,
添加
::respawn:-/bin/login
::sysinit:/etc/init.d/rcS
inittab文件的規則可以網上查閱相關的資料。