徐晨升 張瓊聲 孟祥奎
(中國(guó)石油大學(xué)(華東)計(jì)算機(jī)與通信工程學(xué)院 青島 266580)
在全球超級(jí)計(jì)算機(jī)TOP500強(qiáng)操作系統(tǒng)排行榜中,Linux的占比最近幾十年長(zhǎng)期保持在85%以上,且一直呈現(xiàn)快速上升趨勢(shì)。目前Linux服務(wù)器操作系統(tǒng)在整個(gè)服務(wù)器操作系統(tǒng)市場(chǎng)格局中占據(jù)越來(lái)越多的市場(chǎng)份額,已經(jīng)形成大規(guī)模市場(chǎng)應(yīng)用的局面,尤其在政府、金融、農(nóng)業(yè)、交通、電信等國(guó)家關(guān)鍵領(lǐng)域。同時(shí)Linux在嵌入式系統(tǒng)方面也得到了廣泛應(yīng)用。比如Android 操作系統(tǒng)就是創(chuàng)建在Linux 內(nèi)核之上的。然而,我國(guó)在核心信息技術(shù)產(chǎn)業(yè)方面仍然與國(guó)外有較大差距,比如在cpu 和操作系統(tǒng)領(lǐng)域,尤其是在中興事件之后,發(fā)展自主可控的核心信息技術(shù)產(chǎn)業(yè)已變成一種迫切需求。
本文從Linux 內(nèi)核源代碼的角度深入討論Linux 內(nèi)核是如何在裸機(jī)上被加載運(yùn)行的,同時(shí)介紹了cpu 工作模式[1]的轉(zhuǎn)換以及創(chuàng)建0 號(hào)進(jìn)程并啟動(dòng)它的全過(guò)程。為后續(xù)深入研究Linux內(nèi)核包括內(nèi)存管理、文件系統(tǒng)、進(jìn)程調(diào)度、進(jìn)程通信等核心模塊的實(shí)現(xiàn)與優(yōu)化奠定良好的實(shí)踐和理論基礎(chǔ),希望對(duì)Linux學(xué)習(xí)和使用者有所啟發(fā)。
設(shè)計(jì)好了活動(dòng),那么接下來(lái)在教學(xué)過(guò)程中,老師要引導(dǎo)學(xué)生真正“動(dòng)起來(lái)”.這里不僅僅是指動(dòng)手,更要?jiǎng)幽X,還要調(diào)動(dòng)一切可以調(diào)動(dòng)的感官,這就需要在教師理解學(xué)生的基礎(chǔ)上,設(shè)計(jì)好的問(wèn)題作為活動(dòng)進(jìn)行的紐帶.
周?chē)娜似咦彀松嘟o他出主意給他提醒他都充耳不聞,徑直朝池塘邊走去。突然看見(jiàn)一個(gè)和他年紀(jì)差不多的中年男人,手握一把菜刀朝他齒牙咧嘴比劃著什么。還看見(jiàn)在那人背后的池塘水中,有一個(gè)小女孩水已經(jīng)淹到了胸口,邊哭邊舉起雙臂,一頭拴在女孩手臂上,一頭被持刀人逮在手上的繩子正在不斷搖晃。
進(jìn)入新時(shí)代,我們既看到祖國(guó)繁榮昌盛的一面,同時(shí)也看到諸如“小悅悅”、“江歌”事件,更甚者有現(xiàn)代版的“農(nóng)夫與蛇”的保姆縱火案、“教科書(shū)式耍賴”、紅黃藍(lán)幼兒園虐童事件等等,無(wú)一不在表明,失德現(xiàn)象不再是某一行業(yè)、某一類人的行為,它存在于以“人”為中心所展開(kāi)關(guān)系的方方面面。要解決這一問(wèn)題,就涉及對(duì)人與人之間關(guān)系的重新梳理。而“儒家的人倫思想,即從內(nèi)在的道德客觀化出來(lái),以對(duì)人類負(fù)責(zé)的,始于孝弟,而極于民胞物與,極于以天地萬(wàn)物為一體?!盵1]所以研究《小學(xué)·明倫》思想的意義正是通過(guò)對(duì)人倫關(guān)系的探索,找出人與人和諧相處的途徑,為今天思想政治教育提供借鑒之處。
圖1 展示了linux 內(nèi)核的啟動(dòng)流程。0×7c00 這個(gè)地址來(lái)自intel 的第一代個(gè)人電腦芯片8088,以后的cpu 為了保持兼容,一直使用這個(gè)地址。1981年8 月,IBM 公司最早的個(gè)人電腦IBM PC 5150 上市,就用了這個(gè)芯片,當(dāng)時(shí)搭配的操作系統(tǒng)是86-DOS,這個(gè)操作系統(tǒng)需要的內(nèi)存最少是32KB。即內(nèi)存范圍從0×00000-0×7FFF。8088芯片本身需要占用0×0000-0×03FF,用來(lái)保存各種中斷處理程序,所以內(nèi)存只剩下0×0400-0×7FFF可以使用。
從80386 開(kāi)始,cpu 工作的模式就有兩種,分別是實(shí)模式和保護(hù)模式[3]。剛剛啟動(dòng)的時(shí)候操作系統(tǒng)是運(yùn)行在實(shí)模式的,比如圖1 流程圖中的boot 和setup 程序就是運(yùn)行在實(shí)模式下的。實(shí)模式只能訪問(wèn)1MB 以下的常規(guī)內(nèi)存,而在保護(hù)模式下,全部的32 條地址線有效,可尋址高達(dá)4GB 的物理地址空間。在實(shí)模式下處理器沒(méi)有內(nèi)存保護(hù)概念和多道任務(wù)的工作模式,其尋址方式為“段基址左移4 位+16位地址偏移”得到20位的物理地址,此時(shí)段寄存器中的內(nèi)容是段基址。而在保護(hù)模式下,尋址方式變?yōu)椤岸位?32 位的偏移”,但此時(shí)段寄存器中的內(nèi)容并不是段基址,而是16 位的段選擇子[4],段選擇子是用來(lái)從段描述符表中找段描述符的,從而根據(jù)段描述符得到段基址。段選擇子的結(jié)構(gòu)如圖2所示。
Linux 內(nèi)核版本更新速度很快,一些核心模塊的變化非常大,但是Linux 內(nèi)核加載啟動(dòng)過(guò)程的基本原理變化并不顯著。0.11 版本的內(nèi)核已經(jīng)初步具備現(xiàn)代操作系統(tǒng)的全部功能模塊,是入門(mén)Linux內(nèi)核很好的學(xué)習(xí)材料,因而本文則選擇0.11版本的內(nèi)核作為研究對(duì)象,深入探討分析其加載啟動(dòng)過(guò)程。Linux操作系統(tǒng)可以運(yùn)行在多種不同的硬件平臺(tái)上,比如Arm 和80×86 等平臺(tái)上,由于絕大多數(shù)的Linux 操作系統(tǒng)應(yīng)用于80×86 平臺(tái)上,并且0.11版本的內(nèi)核是基于80×86 的,所以本文討論的也是僅限于80×86平臺(tái)的Linux內(nèi)核的啟動(dòng)過(guò)程。
為了盡量把多的連續(xù)內(nèi)存分配給操作系統(tǒng),bios 負(fù)責(zé)加載引導(dǎo)程序的數(shù)據(jù)(int19h 中斷處理程序,大于512 字節(jié))被放到了內(nèi)存地址的尾部,而引導(dǎo)程序本身需要占用512 個(gè)字節(jié),從而引導(dǎo)程序在內(nèi)存中的起始位置為0×7FFF-512-512+1=0×7c00。
Linux 啟動(dòng)過(guò)程中之所以將system 模塊先移動(dòng)到0×10000 處而不直接移動(dòng)到0×0000 處是因?yàn)槿绻苯右苿?dòng)到0×0000 處,會(huì)導(dǎo)致bios 的部分程序和數(shù)據(jù)被覆蓋,包括一些重要的bios 中斷,而這些中處理程序要在引導(dǎo)程序中使用,所以不能直接移動(dòng)到0×0000 處。由于內(nèi)核數(shù)據(jù)大小不會(huì)超過(guò)512KB,內(nèi)核被加載到了0×10000的位置,所以內(nèi)核物理地址最大不會(huì)到地址0×90000 處。當(dāng)程序轉(zhuǎn)到setup 執(zhí)行的時(shí)候,setup 程序會(huì)獲取系統(tǒng)參數(shù),這些系統(tǒng)參數(shù)被存儲(chǔ)在原先bootsect程序所在的位置,原先的bootsect 程序被覆蓋,而后在setup 程序?qū)ystem模塊從0×10000處移動(dòng)到地址0×0000處,并且設(shè)置了臨時(shí)的GDT 和IDT,為程序跳轉(zhuǎn)到保護(hù)模式做準(zhǔn)備。當(dāng)程序跳轉(zhuǎn)到物理地址0×0000 處執(zhí)行時(shí),head 程序開(kāi)始執(zhí)行,head 程序?qū)⒅匦略O(shè)置GDT 和IDT,并且設(shè)置頁(yè)目錄表和頁(yè)表,同時(shí)設(shè)置CR3寄存器的值并開(kāi)啟分頁(yè),并在堆棧中彈出main()函數(shù)地址,main()函數(shù)開(kāi)始執(zhí)行。
圖1 Linux內(nèi)核啟動(dòng)流程圖
在計(jì)算機(jī)通電之后,固化在CMOS 中的bios 程序會(huì)對(duì)硬件開(kāi)始自檢,之后bios將磁盤(pán)引導(dǎo)扇區(qū)中的bootsect 啟動(dòng)程序(共512 個(gè)字節(jié))加載到內(nèi)存地址0×7c00 處執(zhí)行,bootsect 程序在剛開(kāi)始執(zhí)行時(shí)就把自己移動(dòng)到內(nèi)存地址0×90000 處,開(kāi)始從0×90000 處執(zhí)行之后的程序代碼。bootsect 程序的主要作用是先將存儲(chǔ)在磁盤(pán)上的setup程序加載到內(nèi)存0×90200 處,再把system 內(nèi)核模塊從磁盤(pán)加載到內(nèi)存地址0×10000 處并將控制權(quán)交給setup 程序。setup 程序首先利用bios 提供的中斷程序獲取一些基本的硬件參數(shù),這些參數(shù)被保存在了內(nèi)存地址0×90000 處,也就是之前bootsect 程序所在的位置。接著setup 程序把system 模塊從之前的內(nèi)存地址0×10000 處移動(dòng)到內(nèi)存地址0×00000 處,接著設(shè)置控制寄存器CR0 的第一位切換cpu 的工作模式為保護(hù)模式并跳轉(zhuǎn)到物理地址0×00000 處運(yùn)行。由于在編譯鏈接程序代碼時(shí)head 程序被鏈接到了system 的頭部,因而此時(shí)cpu 開(kāi)始執(zhí)行內(nèi)存地址0×00000 處的head 程序。head 程序主要是初始化中斷描述符表中的256 個(gè)門(mén)描述符[2],檢查A20 地址線是否已經(jīng)打開(kāi),測(cè)試系統(tǒng)是否含有數(shù)學(xué)協(xié)處理器。接著初始化內(nèi)存頁(yè)目錄表,為內(nèi)存的分頁(yè)管理做好準(zhǔn)備,最后跳轉(zhuǎn)到system 模塊中的初始化程序main.c中繼續(xù)執(zhí)行。main.c程序包括各個(gè)內(nèi)核子模塊的初始化,即從main.c 程序開(kāi)始,內(nèi)核才算真正意義上的啟動(dòng)了。Linux 內(nèi)核啟動(dòng)流程圖如圖1 所示。
圖2 段選擇子結(jié)構(gòu)
Linux的每個(gè)進(jìn)程都有與之一一對(duì)應(yīng)的一個(gè)數(shù)據(jù)結(jié)構(gòu),即進(jìn)程控制塊PCB。進(jìn)程控制塊包含了與當(dāng)前進(jìn)程相關(guān)的重要信息,比如進(jìn)程的運(yùn)行狀態(tài)、進(jìn)程所在的代碼段和數(shù)據(jù)段等。Linux內(nèi)核的第一個(gè)進(jìn)程是提前寫(xiě)好的,這個(gè)進(jìn)程也叫0 號(hào)進(jìn)程。main()函數(shù)先保存了根文件系統(tǒng)設(shè)備號(hào),并且指定了內(nèi)存起始地址以及系統(tǒng)主內(nèi)存的容量,而后進(jìn)行各個(gè)方面的初始化,其中就包括調(diào)度程序sched_init()函數(shù)的初始化。該函數(shù)主要設(shè)置了GDT表的第四項(xiàng)和第五項(xiàng)描述符,使它們分別指向0 號(hào)進(jìn)程的任務(wù)狀態(tài)段TSS 段和局部描述符表LDT段。0 號(hào)進(jìn)程的進(jìn)程控制塊數(shù)據(jù)結(jié)構(gòu)叫做INIT_TASK,該數(shù)據(jù)結(jié)構(gòu)的內(nèi)容在sched.h進(jìn)行了初始化,包括初始化了LDT 的表項(xiàng)內(nèi)容以及TSS 的內(nèi)容。緊接著sched_init()函數(shù)將GDT 表第六項(xiàng)之后的表項(xiàng)內(nèi)容全置為NULL,之后加載0 號(hào)進(jìn)程的任務(wù)狀態(tài)段寄存器TR 和局部描述符表LDT,復(fù)位NT以防止執(zhí)行中斷返回指令時(shí)發(fā)生任務(wù)切換而出錯(cuò)。開(kāi)啟時(shí)鐘中斷并設(shè)置了系統(tǒng)調(diào)用中斷門(mén)。調(diào)度程序初始化完畢意味著0 號(hào)進(jìn)程初始化完畢,而后main()函數(shù)執(zhí)行“move_to_user_mode()”語(yǔ)句實(shí)現(xiàn)系統(tǒng)由特權(quán)級(jí)的內(nèi)核態(tài)到用戶態(tài)的切換,使得main()函數(shù)以用戶態(tài)0 號(hào)進(jìn)程的身份接著運(yùn)行。而后0號(hào)進(jìn)程利用fork()函數(shù)創(chuàng)建了進(jìn)程1,利用進(jìn)程1 來(lái)執(zhí)行init 函數(shù),進(jìn)程1 又創(chuàng)建了進(jìn)程2,進(jìn)程3,……進(jìn)程n。至于系統(tǒng)具體運(yùn)行哪個(gè)進(jìn)程,則由調(diào)度程序來(lái)決定某一時(shí)刻應(yīng)該運(yùn)行哪個(gè)進(jìn)程。
圖3 段描述符結(jié)構(gòu)
從head 程序開(kāi)始,cpu 就已經(jīng)工作在保護(hù)模式下了。程序首先將各個(gè)寄存器設(shè)置為立即數(shù)0×10,它是一個(gè)段選擇子,指向臨時(shí)GDT(注意此時(shí)gdtr 中存的GDT 首地址仍然是setup 程序中設(shè)置的臨時(shí)的GDT)的一個(gè)表項(xiàng),其實(shí)是指向臨時(shí)GDT 的數(shù)據(jù)段。接著程序重新設(shè)置新的IDT,將IDT 的256個(gè)表項(xiàng)(每項(xiàng)8字節(jié))全部指向一個(gè)啞中斷門(mén)ignore_int。該中斷處理程序只是打印一個(gè)字符串“Unknown interrupt”,在重新設(shè)置完IDT 后,緊接著重新設(shè)置GDT 表項(xiàng)。接著開(kāi)始檢測(cè)A20 地址線是否開(kāi)啟,并設(shè)置頁(yè)目錄和頁(yè)表項(xiàng)(一個(gè)頁(yè)目錄和4個(gè)頁(yè)表項(xiàng),總共能尋址16MB 大小),而后程序返回到main.c 的入口處,cpu 開(kāi)始執(zhí)行main 程序,進(jìn)行各個(gè)內(nèi)核子模塊的初始化。
先將數(shù)據(jù)段寄存器的值ds 設(shè)置為0×90000,然后開(kāi)始讀取機(jī)器參數(shù)并將其放到0×90000 開(kāi)始的位置,從而bootsect 程序會(huì)被覆蓋。接著讀取硬盤(pán)信息表,由于第一個(gè)硬盤(pán)參數(shù)表的首地址存放在中斷向量0×41 表項(xiàng)中表長(zhǎng)16 個(gè)字節(jié),因而很容易能夠讀取到硬盤(pán)參數(shù)(由于bios中斷向量表是從內(nèi)存地址0×00000 處開(kāi)始存放的,中斷向量表表項(xiàng)大小為4個(gè)字節(jié),因而中斷向量0×41對(duì)應(yīng)的表項(xiàng)的起始地址為4*0×41,從而讀取表項(xiàng)得到第一個(gè)硬盤(pán)參數(shù)首地址并將參數(shù)讀出)。第二個(gè)硬盤(pán)參數(shù)的首地址存放在中斷向量0×46 表項(xiàng)中,因而第二個(gè)硬盤(pán)參數(shù)也很容易讀到。緊接著關(guān)中斷,然后開(kāi)始把system模塊(大小不會(huì)超過(guò)512KB)從0×10000處移動(dòng)到0×00000處。一次移動(dòng)0×8000個(gè)字,即64KB,共移動(dòng)8次即可。移動(dòng)完畢后開(kāi)始加載idtr寄存器和gdtr 寄存器,為跳轉(zhuǎn)到保護(hù)模式做準(zhǔn)備。idt_48是lidt指令的操作數(shù),共6個(gè)字節(jié),其中包含中斷描述表IDT的地址;gdt_48是lgdt指令的操作數(shù),也是6 個(gè)字節(jié),其中包含全局段描述符表的首地址。IDT 和GDT 定義在程序末尾。緊接著程序打開(kāi)A20 地址線,對(duì)8259 中斷控制器重新編程,完成進(jìn)入保護(hù)模式的所有準(zhǔn)備工作。接著設(shè)置寄存器控制寄存器CR0 的第一位為1,導(dǎo)致cpu 工作在保護(hù)模式并跳轉(zhuǎn)到內(nèi)存起始地址0×00000處。
程序一開(kāi)始指定了根文件系統(tǒng)的設(shè)備號(hào),由于bios 直接將bootsect程序加載到了內(nèi)存地址0×7c00處,此時(shí)bootsect 需要先將自己移動(dòng)到內(nèi)存地址0×90000 處。程序入口start 處一開(kāi)始便讓ds 指向0×7c0(不是0×7c00,因?yàn)閷?shí)模式下尋址段寄存器的值會(huì)左移4 位變成0×7c00)處,即bootsect 的源地址,讓es 段指向0×9000,即bootsect 的目標(biāo)地址,而后設(shè)置循環(huán)變量cx 的值為256 個(gè)字,即521 個(gè)字節(jié)。接著令偏移地址si和di為0,使用movw 將512 個(gè)字節(jié)從)0×7c0 處移動(dòng)到0×9000 處,由于代碼段發(fā)生了移動(dòng),也即cs 的值發(fā)生了變化,因而接著設(shè)置各個(gè)段寄存器,讓它們都指向代碼段。接下來(lái)便開(kāi)始加載setup 程序,需要將setup 程序從磁盤(pán)上加載到0×90200 的位置,需要讀4 個(gè)扇區(qū)(起始扇區(qū)為2 號(hào)扇區(qū)),此時(shí)需要調(diào)用int 0×13 中斷。調(diào)用13 號(hào)中斷之前需要將相關(guān)的參數(shù)(比如磁頭號(hào),磁道號(hào)及起始扇區(qū)號(hào),這些參數(shù)是可以通過(guò)邏輯扇區(qū)號(hào)計(jì)算得到)送到寄存器,比如ax,bx等。但此處因?yàn)橐呀?jīng)知道setup 程序是在磁盤(pán)的第二個(gè)扇區(qū),大小為2KB,即4 個(gè)扇區(qū),所以直接調(diào)用int 0×13 即可將setup 程序讀入指定內(nèi)存地址,這個(gè)內(nèi)存地址是由es:bx 指定的,即地址0×90200 處。接著便開(kāi)始獲取磁盤(pán)驅(qū)動(dòng)器參數(shù),還是利用int 0×13 號(hào)中斷,其中主要要保存的參數(shù)是每磁道扇區(qū)數(shù)sectors。接著打印調(diào)用int 0×10 中斷打印Loading system…信息。加載system 模塊信息,調(diào)用read_it將其加載到內(nèi)存地址0×10000:0000 處,加載完system 模塊后直接跳轉(zhuǎn)到setup 程序的起始處,即0×90200:0000處。
由于cpu 現(xiàn)在工作在保護(hù)模式,所以在最后的指令jmpi 0,8 中的8 已經(jīng)是段選擇子,而后cpu 會(huì)根據(jù)這個(gè)段選擇子查找臨時(shí)全局段描述符表,找到它對(duì)應(yīng)的段基址(0×00000 處),由于指令中給出的偏移是0,則此時(shí)cpu 從0×00000 處開(kāi)始執(zhí)行,此時(shí)在這個(gè)地址處的便是head.s的代碼。
從圖3 可以看出一個(gè)段描述符大小為8 個(gè)字節(jié),它指出了段基址和段限長(zhǎng),從而唯一確定了一個(gè)段。這種尋址方式與實(shí)模式下的尋址完全不同,因而要在linux 內(nèi)核啟動(dòng)程序的setup 程序跳轉(zhuǎn)到system 模塊(在內(nèi)存地址0×00000 處)之前要建立一個(gè)臨時(shí)的GDT,然后設(shè)置控制寄存器CR0的第一位為1,進(jìn)入保護(hù)模式。此時(shí)跳轉(zhuǎn)指令中的“段基址”已經(jīng)是保護(hù)模式下的段選擇子,然后根據(jù)該段選擇子查找臨時(shí)的GDT 表,找到對(duì)應(yīng)的段描述符。接著CPU 將直接跳轉(zhuǎn)到該段描述符所指向的段(即0×00000 基址處,)加偏移(0)處執(zhí)行指令,即head.s 程序的開(kāi)始,接著head.s 便開(kāi)始在保護(hù)模式下運(yùn)行。
其中,RPL 表示特權(quán)級(jí),00 表示最高級(jí),11 表示最低級(jí)。TI=0 表示根據(jù)該段選擇子查找的是全局段描述符表GDT[6],TI=1 表示根據(jù)該段選擇子查找的是局部段描述符表LDT。高13 位作為GDT 或者LDT 的索引來(lái)找到相應(yīng)的段描述符。段描述符表的每一個(gè)表項(xiàng)表示一個(gè)段描述符。在整個(gè)系統(tǒng)中,全局段描述符表GDT 只有一張,GDT 可以被放在內(nèi)存的任何位置,但CPU 必須知道GDT 的入口,也就是基地址放在哪里,Intel的設(shè)計(jì)者門(mén)提供了一個(gè)寄存器GDTR用來(lái)存放GDT的入口地址,程序員將GDT 設(shè)定在內(nèi)存中某個(gè)位置之后,可以通過(guò)lgdt指令將GDT 的入口地址裝入此寄存器,從此以后,CPU 就根據(jù)此寄存器中的內(nèi)容作為GDT 的入口來(lái)訪問(wèn)GDT 了。GDTR 中存放的是GDT 在內(nèi)存中的基地址和其表長(zhǎng)界限。局部段描述符表LDT 可以有若干張[7],每個(gè)任務(wù)可以有一張,LDT 本身也是一個(gè)段,在GDT 中也有與之相對(duì)應(yīng)的段描述符,與LDT 相對(duì)應(yīng)的寄存器是LDTR,LDTR 中加載的是GDT 中指向它的描述符的段選擇子。段描述符的結(jié)構(gòu)如圖3所示。
習(xí)近平綠色發(fā)展理論深刻體現(xiàn)了“以人為本”的思想理念。人的自由全面發(fā)展是我黨所有建設(shè)的最終目的,如果因追求單純的經(jīng)濟(jì)增長(zhǎng)而忽略了人的整體利益和人民群眾的地位作用,就本末倒置了。綠色發(fā)展堅(jiān)持人民主體地位,充分肯定了人民群眾在社會(huì)發(fā)展中的主體作用。
move_to_user_mode()是一個(gè)宏定義函數(shù),該函數(shù)是通過(guò)構(gòu)建中斷返回指令所需要的內(nèi)容來(lái)啟動(dòng)0號(hào)進(jìn)程的運(yùn)行的,如圖4所示。
可以看到,該函數(shù)先將0×17 壓棧,地址0×17指向進(jìn)程0 的數(shù)據(jù)段,緊接著將堆棧指針esp 和標(biāo)志寄存器內(nèi)容以及0×0f(該地址指向進(jìn)程0 的代碼段)和標(biāo)號(hào)1的偏移地址(eip)壓棧[10]。再執(zhí)行中斷返回指令iret,該指令將依次把堆棧里的內(nèi)容彈出到eip寄存器,cs段寄存器,標(biāo)志寄存器。但此時(shí)系統(tǒng)發(fā)現(xiàn)cs已指向進(jìn)程0的代碼段,它的特權(quán)級(jí)是3,即特權(quán)級(jí)發(fā)生了變化,則iret 指令會(huì)接著彈出堆棧內(nèi)容到堆棧指針寄存器esp和堆棧段寄存器ss。則在iret指令執(zhí)行完畢后,進(jìn)程0 開(kāi)始運(yùn)行,但可以看到它使用的用戶態(tài)堆棧段依然是原來(lái)系統(tǒng)在內(nèi)核態(tài)運(yùn)行時(shí)的堆棧。
通過(guò)優(yōu)化求解L(β)的最大值,即可求得相應(yīng)的模型參數(shù)β。本文通過(guò)Limited-memory Broyden-Fletcher-Goldfarb-Shanno(L-BFGS)算法數(shù)值求解。
圖4 啟動(dòng)0號(hào)進(jìn)程
本文從源代碼角度詳細(xì)分析了0.11 版本內(nèi)核的啟動(dòng)過(guò)程。如今Linux內(nèi)核的版本已經(jīng)更新到了4.2.0,內(nèi)核的更新主要是針對(duì)內(nèi)核核心模塊的擴(kuò)充與優(yōu)化,使其能夠適用于各種不同的硬件平臺(tái),而Linux 內(nèi)核的啟動(dòng)過(guò)程直至內(nèi)核版本2.6.9 之前并沒(méi)有發(fā)生多大變化,第一個(gè)進(jìn)程的啟動(dòng)仍然是“手動(dòng)”完成的,理解Linux 內(nèi)核的啟動(dòng)過(guò)程,將從實(shí)踐的角度對(duì)Linux 的運(yùn)行機(jī)制有一個(gè)清晰的了解,從而有助于對(duì)Linux 核心模塊的研究與理解打開(kāi)大門(mén)。