#Linux0.11啓動方式算法
##BIOS啓動 當PC電源打開後,80X86結構的CPU將自動進入實模式,並從0xFFFF0開始自動執行程序, 這一般是ROM-BIOS中的地址。PC機的BIOS將執行某些系統檢測,並在物理地址0處開始初 始化中斷向量。此後將可啓動設備的第一個扇區讀入內存絕對地址0x7C00處,並跳轉到這 個地方。shell
##bootsect.s/setup.sbash
###bootsect.s Linux最前面的部分是boot/bootsect.s,它將由BIOS讀入到內存絕對地址0x7C00處, 當它被執行時就會把本身移動到內存絕對地址0x90000(576KB)處,並把啓動設備後 2KB字節代碼boot/setup.s讀入到內存0x90200處,而內核其餘部分(system模塊) 則被讀入到內存地址0x10000(64KB)開始處,所以從機器家電開始的執行順序以下圖。 數據結構
###setup.s 在系統加載期間將顯示信息「Loading...」。而後控制權將傳遞給boot/setup.s,這是 另外一個實模式彙編語言程序。 函數
啓動部分識別主機的某些特性,若是須要會要求用書爲控制檯選擇顯示模式。而後將整 個系統從地址0x10000移至0x0000處,讀取併爲system模塊保留系統參數,以後進入 保護模式並跳轉至系統餘下部分(0x0000處)。加密
##system模塊加載code
###head.s head.s位於system模塊頭部。從這裏開始,內核徹底是在保護模式下運行。這個程序功 能比較單一。首先是加載各個數據段寄存器,重設中斷描述符表idt,使各表項執行一個 只報錯誤的啞中斷子程序ignore_int。而後重設gdt,檢測A20地址線是否已真的開啓, 若是沒開啓進入死循環。檢測數學協處理器。 設置分頁處理機制,將頁目錄表放在絕對物理地址0開始處(本程序會被覆蓋)。最後, head.s利用返回指令將預先放在堆棧中的/init/main.c程序的入口地址彈出,去運行 main()程序。進程
###main.c main.c程序首先利用前面setup.s程序取得的系統參數設置系統的根文件設備號以及一 些內存全局變量。 圖片
####初始化基本環境 內核先進行全部方面的硬件初始化。包括陷阱門、塊設備、字符設備和tty,包括人工設置 第一個任務(task 0)。待全部初始化完成後設置中斷容許標誌開啓中斷,並「手動」切換 到任務0中運行。 內存
####task 0啓動(idle進程) 直接fork()建立task 1,因爲是內核建立的進程沒法使用copy-on-write,又和內核 和以後新建的task 1共用堆棧,所以不能使用堆棧破壞其餘程序運行,因此使用了內 聯函數fork()和pause()。建立完task 1後就開始pause()死循環。
####task 1啓動(init進程)
注意: init進程由用戶態進程idle進程建立,所以可使用copy-on-write, 因此能使用堆棧
在main()中已經進行了系統初始化,包括內存管理、各類硬件設備和驅動程序。init() 函數運行在任務0第一次建立的子進程(任務1)中。它首先對第一個將要執行的程序 (shell)的環境進行初始化,而後以非交互式shell方式加載該程序並執行之(見下方 task 2啓動),並等待task 2結束。
init進程在task 2結束後再次建立新的shell進程,此次是做爲登陸shell運行/bin/sh 的。以後一直等待建立的子進程結束返回,對於所建立的子進程將關閉全部之前還遺留的 句柄(stdin, stdout, stderr),新建立一個會話並設置進程組號,而後從新打開 /dev/tty0做爲stdin,並複製成stdout和stderr。再次執行/bin/sh。
wait()的另外一個功能是處理孤兒進程。若是一個進程的父進程先終止了,那麼這個進程 的父進程就會被設置爲這裏的init進程,並由init進程負責釋放一個已經終止進程的任 務數據結構等資源。
####task 2啓動(/bin/sh) 首次建立
由init進程建立,關閉文件描述符0(stdin),以只讀方式打開/etc/rc文件,並使用execve() 函數將自身替換成/bin/sh程序,而後執行/bin/sh。因爲這裏sh的運行方式是非交互式的,所以 執行完rc文件中的命令後就會當即退出,進程2也隨之結束。
非首次建立
以登陸shell運行,交互式shell。
#較新的啓動方式 在前面的程序上,init()函數直接開始執行了命令解釋程序(shell程序)/bin/sh,而 在實際可用的系統中卻並不是如此。爲了能具備登陸系統的功能和多人同時使用系統的能力, 一般的系統是在這裏或相似地方執行系統初始化程序init.c,而此程序會根據系統/etc/ 目錄中配置文件的設置信息,對系統中支持的每一個終端設備建立子進程,並在子進程中運 行終端初始化設置程序agettty(統稱getty),gettty程序則會在終端上顯示用戶的登 錄提示信息「login:」。當用戶鍵入了用戶名後,getty被替換成login程序。login程序驗 證了用戶輸入口令的正確性後,最終調用shell程序,並進入shell交互工做界面。
init進程的主要任務是根據/etc/rc文件中設置的信息,執行其中設置的命令,而後根據 /etc/inittab文件中的信息,爲每個容許登陸的終端設備使用fork()建立一個子進程, 並在每一個新建立的子進程中運行getty。而init進程則調用wait(),進入等待子進程結束 狀態。 每當一個子進程結束退出,就會根據返回的pid知道哪一個對應終端的子進程結束了,所以 就會爲相應終端設備再建立一個新的子進程。每一個容許的終端設備都是中有一個對應的進 程爲其等待處理。
當系統關閉時,init負責殺死全部其餘進程,卸載全部的文件系統以及中止處理器的工 做,以及它配置成要作的工做。
##getty程序 gettty程序主要任務是設置終端類型、屬性、速度和線路規程。它打開並初始化一個tty 端口,顯示提示信息,並等待用戶鍵入用戶名。該程序只能由超級用戶執行。一般,若 /etc/issue文本文件存在,則getty會首先顯示其中的文本信息,而後顯示登陸提示信 息,讀取用戶輸入的登陸名,並執行login程序。
##login程序 login程序主要用於要求登陸用戶輸入密碼。根據用交輸入的用戶名,它從口令文件passwd 中取得對應用戶的登陸項,而後調用getpass()以顯示「password:」提示信息,讀取用戶 鍵入的密碼,而後使用加密算法對鍵入的密碼進行加密處理,並與口令文件中該用戶項中 pw_passwd字段做比較。若是登陸失敗,login程序會退出執行並被父進程init的wait() 接收,init會根據記錄下來的信息再次建立一個子進程,並在進程中針對該終端設備再次 執行getty。
若是用戶鍵入的密碼正確,則login會把當前工做目錄修改爲口令文件中指定的用戶起始 目錄。並把對應終端的訪問權限修改爲用戶讀/寫和組寫,設置進程的組ID。而後利用所得 的信息初始化環境變量信息,如(HOME=)。接着顯示/etc/motd(message-of-the-day) 文件中的文本信息,並檢查並顯示該用戶是否有郵件的信息。最後login程序改變成登陸 用戶的用戶ID並執行用戶口令文件中該用戶項指定的shell程序,如bash後csh等。若是 沒有指定shell,則會使用默認/bin/sh。
##shell程序 在登陸過程當中login開始執行shell時,所帶參數argv[0]的第一個字符是‘-’,表示該 shell做爲一個登陸shell被執行。此時shell程序會根據該字符,執行某些與登陸過程 相應的操做。登陸shell首先從/etc/profile文件以及.profile文件(若存在)讀取 命令並執行。若是在進入shell時設置了ENV環境變量或者在.profile中設置了該變量, 則shell下一步會從該變量命名的文件中讀取命令並執行。所以用戶應該把每次登陸是 都要執行的命令放在.profile文件中,而把每次運行shell都要執行的命令放在ENV變 量指定的文件中。
如放在.profile中:
ENV=$HOME/.anyfilename; export ENV