2.5 MINIX 進程概述
以MINIX為例說明進程管理购撼、進程間通信以及進程調度的原理。MINIX與UNIX不同凶赁,UNIX的核心是一個不分模塊的單塊程序颅拦,而MINIX本身就是一組進程的集合,它們相互之間怖侦、以及與用戶進程之間使用進程間通信機制 - 消息傳遞來進行通信篡悟。優(yōu)點:結構更加模塊化和靈活。例如匾寝,這使得很容易就可以將整個文件系統(tǒng)替換成另一個完全不同的文件系統(tǒng)搬葬,而無需重新編譯核心。
2.5.1 MINIX 的內部結構
最底層:捕獲所有的中斷和陷入急凰,完成進程調度,并向高層提供一個采用消息進行通信的獨立順序進程模型猜年。該層的代碼有兩大主要功能抡锈。第一是捕獲陷入和中斷、保存和恢復寄存器乔外、調度以及向高層提供一個獨立順序進程模型床三。第二是處理消息機制:檢查目標進程的合法性、定位物理內存中的發(fā)送和接收緩沖區(qū)杨幼、以及從發(fā)送方向接收方拷貝數據勿璃。其中中斷處理的最底層部分用匯編語言編寫,其余部分和其他層次用C語言編寫。
第二層包括I/O進程补疑,每類設備都有一個I/O進程歧沪。為了將其與普通用戶進程相區(qū)別,我們稱之為任務(tasks)莲组。但任務與進程間的差別微乎其微诊胞。在許多系統(tǒng)中I/O任務被稱作設備驅動程序(device driver)。這里“任務”和“設備驅動程序”可以換用锹杈。每一類設備都需要一個任務撵孤,包括磁盤、打印機竭望、終端邪码、網絡接口以及時鐘。如果有其他I/O設備咬清,則它們也需要相應的任務闭专。有一個任務 - 系統(tǒng)任務有些與眾不同,它不對應于任何I/O設備旧烧,我們將在下一章對這些任務進行討論影钉。第二層的所有任務和第一層的代碼鏈接成一個單一的二進制程序,稱作核心(kernel)掘剪。某些任務共享公共的子例程平委,但它們相互之間完全獨立,分別進行調度夺谁,并采用消息進行通信廉赔。從286開始的Intel處理器為每個進程賦予四種特權級中的一種。盡管任務與核心被編譯在一起匾鸥,但當核心和中斷處理程序被執(zhí)行時蜡塌,它們被賦予較任務更高的特權級。所以真正的核心代碼可以訪問任一部分內存扫腺,以及任一處理器寄存器 - 實質上岗照,核心可以使用系統(tǒng)中任何地方的數據執(zhí)行任何指令。任務不能執(zhí)行全部的機器指令笆环,也不能訪問所有CPU寄存器或所有的內存攒至。但在為較低特權級的進程執(zhí)行I/O時,任務可以訪問屬于這些進程的內存區(qū)域躁劣。有一個任務 - 系統(tǒng)任務迫吐,它并不執(zhí)行一般意義的I/O,其作用提供某些特定服務账忘,例如當進程本身不允許在不同的內存區(qū)域間進行拷貝時志膀,由系統(tǒng)任務執(zhí)行此操作熙宇。當然在不提供多特權級的機器上,例如老式的Intel處理器溉浙,無法強制執(zhí)行這些限制烫止。
第三層包含向用戶進程提供有用服務的進程。這些服務器進程在低于核心和任務的特權級上運行戳稽,不能直接訪問I/O端口馆蠕。它們也不能訪問屬于自己的段以外的內存。內存管理器(Memory Manager - MM)負責執(zhí)行所有牽涉到內存管理的系統(tǒng)調用惊奇,如FORK互躬、EXEC和BRK。文件系統(tǒng)(File System - FS)負責執(zhí)行文件系統(tǒng)的調用颂郎,READ吼渡、MOUNT和CHDIR。此處正適于指出盡管服務器是獨立的進程乓序,但它們和用戶進程有一點不同寺酪,即它們在系統(tǒng)啟動的同時被啟動,而且在系統(tǒng)活躍期間不會終止竭缝。此外房维,盡管從它們禁用的機器指令來看沼瘫,它們與用戶進程運行在相同的特權級上抬纸,但它們的執(zhí)行優(yōu)先級比用戶進程高。為了加入一個新的服務器耿戚,核心必須重新編譯湿故。核心的啟動代碼在用戶進程開始運行之前將服務器進程安裝在進程表的特權表項中。
第四層包含所有的用戶進程 - shell膜蛔、編譯器坛猪、編輯器以及用戶的a.out程序。一個運行系統(tǒng)通常有一些進程在系統(tǒng)引導時啟動皂股,并一直運行墅茉,例如,一個精靈程序就是一個周期性運行或總是等待某個事件(例如網絡上一個包的到達)的后臺進程呜呐。從某種意義上說就斤,精靈進程是一個單獨啟動而作為一個用戶進程運行的服務器。但與裝入特權進程表項的真正的服務器不同蘑辑,這些程序無法象內存和文件服務器那樣受到核心的特殊對待洋机。
2.5.2 MINIX 中的進程管理
MINIX中的進程遵從本章前邊所描述的通用進程模型。整個系統(tǒng)中所有的用戶進程都屬于以init(見圖2 - 26)為根節(jié)點的一棵進程樹洋魂。
1绷旗、當計算機開機時喜鼓,硬件從引導盤上將第一道第一扇區(qū)讀入內存并從那里開始執(zhí)行。
軟盤:第一扇區(qū)包含了引導程序(bootstrap)衔肢。引導程序很小庄岖,因為它必須能容納在一個扇區(qū)里。MINIX引導程序裝入一個更大的程序boot角骤,由boot裝入操作系統(tǒng)顿锰。
硬盤:硬盤需要一個中間步驟。硬盤被分成若干分區(qū)(partition)启搂,整個硬盤的第一個扇區(qū)包括一段小程序和磁盤分區(qū)表硼控,通常稱為主引導記錄。程序部分被執(zhí)行以讀入分區(qū)表并選擇活躍分區(qū)胳赌±魏常活躍分區(qū)的第一個扇區(qū)有一個引導程序,它隨后被裝入并執(zhí)行以查找并啟動程序boot疑苫,這與從軟盤引導完全相同熏版。
軟盤啟動過程:引導程序<第一扇區(qū)> --?裝入boot程序 --?由boot裝入操作系統(tǒng)。
硬盤啟動過程:主引導<硬盤第一個扇區(qū):小段程序+磁盤分區(qū)表> --?選擇活躍分區(qū)<程序啟動捍掺,讀取分區(qū)表> --?活躍分區(qū)<第一個扇區(qū)引導程序> --?查找并啟動程序boot -- boot裝入操作系統(tǒng)撼短。
boot將在軟盤或硬盤分區(qū)上找一個包含多個部分的文件,并將各部分裝到內存的適當位置挺勿。這些部分包括核心曲横、內存管理器、文件系統(tǒng)以及init - 第一個用戶進程不瓶。這個啟動過程并不簡單禾嫉。所有那些屬于磁盤任務和文件系統(tǒng)范圍的操作在這兩部分活躍之前都要由boot完成。一旦裝入操作完成蚊丐,核心便開始運行熙参。
2、初始化階段麦备,核心先啟動各任務孽椰,然后是內存管理器、文件系統(tǒng)及所有在第三層運行的服務器凛篙。
當所有這些都開始運行并完成初始化之后黍匾,它們將阻塞,等待執(zhí)行某種操作鞋诗。當所有的任務和服務器被阻塞之后膀捷,將執(zhí)行第一個用戶進程 - init。init已經位于內存削彬,不過在它啟動時其他所有部分均已就位全庸,所以它可以作為一個獨立的程序從磁盤上裝入秀仲。但是,由于init只啟動一次壶笼,而且不會再從盤上裝入神僵,所以最簡便的方法是把它與核心、任務和服務器一起包括在系統(tǒng)的映像文件中覆劈。
init啟動后先讀/etc/ttytab文件保礼,該文件列出了所有可能的終端設備。那些可以作為登錄終端(在標準MINIX中责语,只包括控制臺)的設備都在/etc/ttytab的getty域有一項炮障。而且init為每個這樣的終端創(chuàng)建一個子進程。通常每個子進程都執(zhí)行文件/usr/bin/getty坤候,打印出一條信息胁赢,然后等待輸入一個用戶名。隨后將該用戶名作為參數來調用 /usr/bin/login白筹。如果某個終端需要特殊的處理(例如智末,一條撥號線),則/etc/ttytab可以指定一條命令(如/usr/bin/stty)徒河,在執(zhí)行getty之前執(zhí)行該命令并對線路進行初始化系馆。
在成功地登錄之后,/bin/login執(zhí)行用戶的shell(在/etc/passwd文件中指定顽照,通常是/bin/sh或/usr/bin/ash)由蘑。shell等待用戶鍵入命令,并為每條命令創(chuàng)建一個新的進程棒厘。采用這種方式時纵穿,各個shell都是init的子進程下隧,而用戶進程則是init的孫子進程奢人,并且所有的用戶進程都是一棵進程樹的組成部分。
MINIX用于進程管理的兩條最重要的系統(tǒng)調用是FORK和EXEC淆院。FORK是創(chuàng)建一個新進程的唯一途徑何乎。EXEC允許一個進程執(zhí)行一個指定的程序,當一個程序被執(zhí)行時土辩,將按照文件頭中指定的大小為其分配一部分內存支救。盡管在進程運行期間,數據段拷淘、棧段和空閑未使用部分的大小可以不時地改變各墨,但進程分配到的內存總量將保持不變。
一個進程的所有信息被保存在進程表中启涯,進程表劃分成核心贬堵、內存管理器和文件系統(tǒng)三部分恃轩,分別擁有它們各自所需要的那些域。當出現一個新進程(通過FORK)黎做,或者一個老進程結束(通過EXIT或信號)時叉跛,內存管理器首先更新它那部分進程表,然后向文件系統(tǒng)和核心發(fā)送消息蒸殿,以通知它們進行相應的操作筷厘。
2.5.3 MINIX 中的進程間通信
MINIX提供了三條原語來發(fā)送和接收消息,它們均通過C庫例程調用宏所。其中
send(dest酥艳,&message)用來向進程dest發(fā)送一條消息;
receive(source爬骤,&message)用來從進程source(或任何地方)接收一條消息玖雁;
send_rec(src_dst,&message)用來發(fā)送一條消息盖腕,并等待同一個進程的應答赫冬。
以上調用中第二個參數是消息數據的本地地址。核心中的消息傳遞機制將消息從發(fā)送者拷貝到接收者溃列。應答消息(對于send_rec)將覆蓋原先的消息劲厌。原則上該核心機制可以替換為另一套機制以實現分布式系統(tǒng),即在網絡上將消息從一臺機器拷貝到另一臺機器上听隐。但在實踐中這很復雜补鼻,因為有時消息的內容可能是一個指向大型數據結構的指針,于是分布式系統(tǒng)也必須提供網絡上數據本身的拷貝功能雅任。每個進程或任務都可以從/向同層和下一層中的進程或任務發(fā)送和接收消息风范,用戶進程不能直接與I/O任務通信,系統(tǒng)強制地執(zhí)行這一限制沪么。當一個進程(作為特例硼婿,這里也包括任務)向一個當前未在等待消息的進程發(fā)送一條消息時,發(fā)送者將阻塞禽车,直到目標進程執(zhí)行receive寇漫。換言之,MINIX使用會合的方法來避免對已發(fā)送而未接收到的消息進行緩沖的問題殉摔。盡管這沒有帶緩沖的方案靈活州胳,但事實證明對MINIX來說它已經足夠了,而且由于不需要緩沖管理逸月,所以簡單許多栓撞。
2.5.4 MINIX 中的進程調度
中斷系統(tǒng)使多道程序操作系統(tǒng)持續(xù)不斷地工作。當進程請求輸入時碗硬,它們將阻塞以允許其他進程執(zhí)行瓤湘。當輸入可用時捌归,當前運行進程被磁盤、鍵盤或其他硬件中斷岭粤。時鐘也產生中斷惜索,這種中斷使正在運行的未請求輸入的用戶進程最終放棄CPU,以使其他進程獲得運行的機會剃浇。MINIX最底層軟件的任務就是通過將中斷轉換成消息來對其加以隱藏巾兆。就進程(以及任務)而言,當一個I/O設備完成一個操作時虎囚,它向某些進程發(fā)送一條消息角塑,將其喚醒并使之成為就緒。
每當一個進程被中斷時淘讥,不管中斷源是常規(guī)的I/O設備還是時鐘圃伶,都有機會重新確定哪個進程最需要運行機會。當然蒲列,在一個進程終止時也要執(zhí)行該操作窒朋,但在類似MINIX這樣的系統(tǒng)中,由I/O操作和時鐘引起的中斷遠遠多于進程終止的情況蝗岖。MINIX調度程序使用一個三級排隊系統(tǒng)侥猩,分別對應于圖2-26中的第2、3抵赢、4層欺劳。任務和服務器一級的進程一直運行直到阻塞,而用戶進程則采用時間片輪轉調度铅鲤。任務具有最高優(yōu)先級划提,內存管理器和文件管理器次之,用戶進程最低邢享。
當調度程序選擇一個進程來運行時鹏往,它首先檢查是否有就緒的任務,如果有一個或多個驼仪,則隊首的那個將運行掸犬。如果沒有任務就緒,則檢查并運行服務器進程(MM或FS)绪爸。若沒有合適的服務器進程,則運行一個用戶進程宙攻。如果沒有進程就緒奠货,則選擇IDLE進程。這個循環(huán)一直執(zhí)行到下一個中斷到來座掘。在每一個時鐘滴答递惋,都將檢查當前進程是否是一個運行超過100毫秒的用戶進程柔滔。如果是,則調用調度程序來查看是否有另一個用戶進程在等待CPU萍虽,如果發(fā)現一個這樣的進程睛廊,則當前進程被移到隊列的末尾,而運行當前的隊首進程杉编。任務超全、內存管理器和文件系統(tǒng)不會被時鐘剝奪,不論它們已運行了多久邓馒。