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.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM