Linux文件系統啓動過程及login的實現

1. busybox簡介

busybox是一個集成了一百多個最常用linux命令和工具的軟件,它將許多常用的LINUX命令和工具結合到了一個單獨的可執行程序中。雖然與相應的GNU工具比較起來,busybox所提供的功能和參數略少,但在比較小的系統(例如啓動盤)或者嵌入式系統中,已經足夠了。

    busybox在設計上就充分考慮了硬件資源受限的特殊工作環境。它採用一種很巧妙的辦法減少自己的體積:所有的命令都通過“插件”的方式集中到一個可執行文件中,在實際應用過程中通過不同的符號鏈接來確定到底要執行哪個操作。例如最終生成的可執行文件爲busybox,當爲它建立一個符號鏈接ls的時候,就可以通過執行這個新命令實現列目錄的功能。採用單一執行文件的方式最大限度地共享了程序代碼,甚至連文件頭、內存中的程序控制塊等其他操作系統資源都共享了,對於資源比較緊張的系統來說,是最合適不過了。

Busybox配置如下:

Build Options--->

[*] Build BusyBox as a static binary (no shared libs)

Installation Options --->
Login/Password Management Utilities --->

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的時候把相關的選項選上,就可以支持用戶密碼登錄了,見下圖。
Linux文件系統啓動過程及login的實現 - sunnyshineboy - sunnyshineboy的博客
    編譯好busybox之後,在可執行文件中,可以找一個文件名爲login的軟鏈接,就是負責用戶驗證登錄的。
    可以直接拷貝主機的passwd 和 shadow 兩個文件到開發板的/etc目錄下.
    在/etc/init.d/rcS文件中加入 
    /bin/login
    重啓開發板,登錄就需要密碼驗證了
    
    還可以在/etc創建一個inittab文件,
    添加
    ::respawn:-/bin/login
    ::sysinit:/etc/init.d/rcS
    inittab文件的規則可以網上查閱相關的資料。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章