實現(xiàn)一個最簡單的嵌入式操作系統(tǒng)
實現(xiàn)一個最簡單的嵌入式操作系統(tǒng)(一)
實現(xiàn)一個什么都不能做的嵌入式操作系統(tǒng)
1.首先確定CPU,在這里為了簡單,就選用嵌入式的CPU,比如ARM系列,之所以用RISC(簡單指令集)類型的CPU,其方便之處是沒有實模式與保護模式之分,采用線性的統(tǒng)一尋址,也就是不需要進行段頁式內(nèi)存管理,還有就是芯片內(nèi)部集成了一些常用外設控制器,比如以太網(wǎng)卡,串口等等,不需要像在PC機的主板上那么多外設芯片
2.確定要實現(xiàn)的模塊和功能,為了簡單,只實現(xiàn)多任務調(diào)度(但有限制,比如最多不超過10),實現(xiàn)中斷處理(不支持中斷優(yōu)先級),不進行動態(tài)SHELL交互,不實現(xiàn)動態(tài)模塊加載,不實現(xiàn)fork之類的動態(tài)進程派生和加載(也就是說要想在你的操作系統(tǒng)上加入用戶程序,只能靜態(tài)編譯進內(nèi)核中;不支持文件系統(tǒng),不支持網(wǎng)絡,不支持PCI,USB,磁盤等外設(除了支持串口,呵呵,串口最簡單嘛),不支持虛擬內(nèi)存管理(也就是說多任務中的每個進程都可以訪問到任何地址,這樣做的話,一個程序
死了,那么這個操作系統(tǒng)也就玩完了)
3.確定要使用的編譯器,這里采用GCC,文件采用ELF格式,當然,最終的文件就是BIN格式,GCC和LINUX有著緊密的聯(lián)系,自己的操作系統(tǒng),需要C庫支持和系統(tǒng)調(diào)用支持,所以需要自己去裁剪C庫,自己去實現(xiàn)系統(tǒng)調(diào)用
4.實現(xiàn)步驟:首先是CPU選型,交叉編譯環(huán)境的建立,然后就是寫B(tài)OOTLOADER,寫操作系統(tǒng)
實現(xiàn)一個最簡單的嵌入式操作系統(tǒng)(二)
如何實現(xiàn)BOOTLOADER
1.之所以要實現(xiàn)一個專用的BOOTLOADER,一是為了更好的移植和自身的升級,二是為了方便操作系統(tǒng)的調(diào)試,當然,你完全可以將這部分所要實現(xiàn)的與操作系統(tǒng)相關的功能集成到操作系統(tǒng)中去
2.確定一個簡單的BOOTLOADER所要完成的功能:我們這里只需要完成兩個主要功能,一是將操作系統(tǒng)加載到內(nèi)存中去運行,二是將自己和操作系統(tǒng)內(nèi)核固化到ROM存儲區(qū)(這里的ROM可以是很多設備,比如嵌入式芯片中的FLASH,PC機上的軟盤,U盤,硬盤等)
3.BOOTLOADER的編寫:
第一步:要進行相關硬件的初使化,比如在at91rm9200這塊嵌入式板子上(以后都使用這一款芯片,主要是我對這款芯片比較熟悉,嘿嘿),大概要做接下來的幾方面的工作,其一:將CPU模式切換進系統(tǒng)模式,關閉系統(tǒng)中斷,關閉看門狗,根據(jù)具體情況進行內(nèi)存區(qū)域映射,初始化內(nèi)存控制區(qū),包括所使用的內(nèi)存條的相關參數(shù),刷新頻率等,其二:設定系統(tǒng)運行頻率,包括使用外部晶振,設置CPU頻率,設置總線頻率,設置外部設備所采用的頻率等。其三:設置系統(tǒng)中斷相關,包括定時器中斷,是否使用FIQ中斷,外部中斷等,還有就是中斷優(yōu)先級設置,這里只實現(xiàn)兩個優(yōu)先級,只有時鐘中斷高一級,其它都一樣,而中斷向量初始化時都將這些中斷向量指向0x18處,并關閉這里的所有中斷,如果板子還接有諸如FLASH設備的話,還需要設置諸如FLASH相關操制寄存器,其四:需要關閉CACHE,到此為止,芯片相關內(nèi)容就完成初始化了
第二步:中斷向量表,ARM的中斷與PC機芯片的中斷向量表有一點差異,嵌入式設備為了簡單,當發(fā)生中斷時,由CPU直接跳入由0x0開始的一部分區(qū)域(ARM芯片自身決定了它中斷時就會跳入0x0開始的一片區(qū)域內(nèi),具體跳到哪個地址是由中斷的模式?jīng)Q定的,一般用到的就是復位中斷,F(xiàn)IQ,IRQ中斷,SWI中斷,指令異常中斷,數(shù)據(jù)異常中斷,預取指令異常中斷),而當CPU進入相應的由0x0開始的向量表中時,這就需要用戶自己編程接管中斷處理程序了,這就是需要用戶自己編寫中斷向量表,中斷向量表里存放的就是一些跳轉指令,比如當CPU發(fā)生一個IRQ中斷時,就會自動跳入到0x18處,這里就是用戶自己編寫的一個跳轉指令,假如用戶在此編寫了一條跳轉到0x20010000處的指令,那么這個地址就是一個總的IRQ中斷處理入口,一個CPU可能有多個IRQ中斷,在這個總的入口處如何區(qū)分不同的中斷呢?就由用戶編程來決定了,具體實現(xiàn)請參見以后相關部分,中斷向量表的一般用一個vector.S文件,當然,如何命名那是你自己的喜愛,但有一點需要聲明,那就是在鏈接時一定要將它定位在0x0處
第三步:設置堆棧,一般使用三個棧,一個是IRQ棧,一個是系統(tǒng)模式下的棧(系統(tǒng)模式下和用戶模式共享寄存器和內(nèi)存空間,這主要是為了簡單),設置棧的目的主要是為了進行函數(shù)調(diào)用和局部變量的存放,不可能全用匯編,也不可能不用局部變量
第四步:將自己以后的代碼段和數(shù)據(jù)段全部拷貝至內(nèi)存,并將BSS段清零
第五步:進行串口的初始化(主要是為了與用戶交互,進行與PC機的文件傳輸),F(xiàn)LASH的初始化這里在FLASH中存放BOOT和內(nèi)核),F(xiàn)LASH驅動的編寫(這里的驅動有別于平常所說的驅動,由于FLASH不像SDRAM,只要設定了相關控制器之后就可以直接讀寫指定地址的數(shù)據(jù),對FLASH的寫操作是一塊一塊數(shù)據(jù)進行,而不是一個字節(jié)一個字節(jié)地寫,具體請查閱相關資料)
第六步:等待一定的秒數(shù),來接收用戶進行輸入,如果在指定的秒數(shù)內(nèi)用戶未輸入任何字符,那么
BOOT就開始在FLASH中的指定位置(可以由自己指定,這么做主要是為了簡單)讀取內(nèi)核的所有數(shù)據(jù)到內(nèi)存中(具體是內(nèi)存中的什么位置由自己指定,也可以采用LINUX之類的做法,就是在內(nèi)存的起始位置加上一個0x8000處),將跳轉到內(nèi)核的第一條代碼處);如果用戶在指定的秒數(shù)內(nèi)鍵入了字符(這主要是為了方便開發(fā),如果開發(fā)定型之后完全可以不要這段代碼),那么就在串口與用戶進行交互,接受用戶在串口輸入的命令,比如用戶要求下載文件在FLASH中指定的位置等,具體內(nèi)容可參考U-BOOT之類的開源項目到這里為止,BOOT部分已完成,這個BOOT非常簡單,僅僅只是將PC機上傳下來的文件固化到FLASH中,然后再將FLASH中的操作系統(tǒng)內(nèi)核部分加載進內(nèi)存中,并將CPU的控制權交給操作系統(tǒng),下一頁開始講解如何寫一個最簡單的操作系統(tǒng),呵,到現(xiàn)在才開始切入正題呢!?。?!
實現(xiàn)一個最簡單的嵌入式操作系統(tǒng)(三)
如何實現(xiàn)一個最簡單的操作系統(tǒng)
這里為了簡單,就不考慮可移植性開求,不從BOOT部分來接收參數(shù),也不對硬件進行檢測,
也不需要進行DATA段,代碼段的重定位。我只是讀了LINUX內(nèi)核相關部分,并未自己去實現(xiàn)
一個操作系統(tǒng),所以我以下所說的只是概念性的東西:
1.接管系統(tǒng)的中斷處理,由于BOOT部分的代碼決定了那個中斷向量表,從而決定了系統(tǒng)中斷
之后進入的內(nèi)存位置,但BOOT并不知道操作系統(tǒng)的中斷處理函數(shù)位置所在啊,怎么辦呢?
有幾種方法,其一是:如果你的板子可以重映射地址,也就是可以將內(nèi)存條所在的位置
重映射成0x0開始,那么在鏈接內(nèi)核的時候,就將操作系統(tǒng)自己的中斷向量表定位在0x0處
并且在BOOTLOADER引導結束時就完成映射操作,并讓CPU跳轉到0x0處執(zhí)行;如果沒有重映
射功能,我就不曉得怎么辦了,不過我想到一個折衷的辦法,就是在BOOTLOADER啟動完成
時(也就是將CPU控制權交給操作系統(tǒng)內(nèi)核時),重新改寫FLASH的0x0區(qū)域,就是將操作
系統(tǒng)的內(nèi)核的中斷向量表寫入FLASH區(qū)的0x0處,比如,當一個IRQ發(fā)生時,CPU決定了會
跳入0x18(假設這里FLASH占用地址總線0x0至0x0fffffff,內(nèi)存占用0x20000000至0x2fffffff)
,而BOOTLOADER在最后將0x18處的代碼修改成了0x20000000加上0x18的地址處的代碼,而這個
地址就是內(nèi)核的中斷向量表中的相關跳轉指令,就相當于跳轉進了內(nèi)核所關聯(lián)的IRQ處理函數(shù)
的地址上去執(zhí)行中斷處理函數(shù)了,而這樣的不好之處在于:當系統(tǒng)重新上電之后,BOOT的
中斷向量表已經(jīng)被修改,除非BOOT本身不使用中斷,呵,在這樣簡單的系統(tǒng)中,BOOT是不
需要中斷功能的
2.這里為了簡單,所以沒有使用分頁內(nèi)存管理,就不需要建立頁表等操作,直接進行操作
系統(tǒng)的堆棧設置,同BOOT一樣的設置過程一樣,接著就進行BSS段清零操作,這里的BSS段
是指操作系統(tǒng)自身的BSS段,與BOOT的BSS段是同一個含義只是用在了不同的地方了,接著
就跳入了MAIN函數(shù)
3.為了最大可能的簡單,采用靜態(tài)建立任務結構數(shù)組,比如只建立十個任務,那么首先要
為這十個任務結構分配段內(nèi)存,可以在堆上分配(這個分配的內(nèi)存直到操作系統(tǒng)結束才會
被釋放,當然也可以指定一片操作系統(tǒng)的其它地方都用不到的內(nèi)存區(qū)域,不過這樣寫的話
就有點外行的味道了,而符務結構數(shù)組的指針卻是全局變量,存放在BSS段或者DATA段),
由于在上一步中已經(jīng)分配了一個系統(tǒng)堆棧,那么我們這十個任務就分享這總體的堆棧區(qū)域
這里的重點就是如果定義每個任務結構數(shù)組里面的結構,可以參照LINUX的相關部分設計
4.中斷處理:在第一步中已經(jīng)確定了CPU進行相關的幾類型的中斷跳轉地址,而相同類型
的中斷卻只有一個入口地址,這里的中斷處理就會完成以幾個動作:
其一:入棧操作,包括所有寄存器入棧,至于這個棧,就是在第二步中所設置的IRQ棧,
其二:屏掉所有中斷,呵,這里為了簡單起見,所以在處理中斷時不允許再次發(fā)生中斷
其三:讀取中斷相關的寄存器,判別是發(fā)生了什么中斷,以至于跳進相關的中斷處理函
數(shù)中去執(zhí)行(在這里只包括兩種中斷,一是時鐘中斷,另一個是SWI中斷,也就是所謂
的系統(tǒng)調(diào)用時需要用到的)
其四:等待中斷處理完成,然后就開啟中斷并出棧,恢復現(xiàn)場,將CPU控制權交給被中斷
的代碼處
注意:
其一:在MIAN中必須首先確定整個系統(tǒng)有哪些需要處理的中斷,也就是有哪些中斷處理
函數(shù),然后才編寫這里的中斷處理函數(shù)
其二:本操作系統(tǒng)不處理虛擬內(nèi)存,其至連CPU異常都不處理(一切都為了簡單),一旦
發(fā)生異常,系統(tǒng)就死機
5.對TIMER的實現(xiàn),首先確定時間片,為了讓系統(tǒng)更穩(wěn)定,而且我們不需要實時功能,盡
可能讓時間片設置長一點,比如我們讓一個任務運行20個時鐘滴答數(shù),然后應根據(jù)系統(tǒng)
頻率來確定每個系統(tǒng)滴答所占用的毫秒,這里使用5毫秒讓系統(tǒng)定時器中斷一次,那么就
需要寫時鐘寄存器,具體參閱芯片資料,計算下來,一個任務最大可能連續(xù)運行100毫秒
,注意:我們的操作系統(tǒng)不支持內(nèi)核搶占,同時只支持兩級中斷優(yōu)先級,就是只有時鐘
中斷的優(yōu)先級高一點,其它的優(yōu)先級都低一級,但是在中斷處理一節(jié)中卻屏掉了這個功能
因為一進入中斷處理,就禁止中斷,所以不管其它中斷優(yōu)先級有多高都沒有用的,這樣做
優(yōu)點是簡單了,但不好之處顯而易見,特別在相關中斷處理函數(shù)如果進入了死循環(huán),那么
整個系統(tǒng)就死了,而且時間片也變得不準確了,反正都不用實時,也不需要實時鐘支持嘛
至于中斷優(yōu)先級設置請參閱芯片資料
6.進程調(diào)度的實現(xiàn),也就是do_timer函數(shù)(時鐘中斷處理函數(shù)),有一個全局變量指針,
指向的就是當前任務結構數(shù)組(或者鏈表),當時鐘中斷時,就進入此函數(shù)中,首先判斷
任務結構體中的時間片是否用完,如未用完,就減一,然后退出中斷,讓CPU繼續(xù)運行當
前的任結構,若用完了時間片,就重置時間片,并重新尋找任何結構數(shù)組中的下一個等待
運行的任務,若找到了,就切換至新的任務,至于如何切換,請見下一頁描述,如果未找
到就切換到IDLE任務(類似于LINUX,呵呵,所有的處理就是模仿LINUX,由于本人水平太
差,所就不能自創(chuàng)一招),注意:為了簡單,所以沒有實現(xiàn)任務優(yōu)先級,也未實現(xiàn)任務
休眠等,也就是說只要靜態(tài)地決定了有十個任務,這十個任務就按先后順序一個一個執(zhí)行
而且每個任務都不允許結束,就是說在每個進程中的最后一句代碼都必須用死循環(huán),不然
的話系統(tǒng)就跑飛了),還有一點,進程不支持信號,沒有休眠與喚醒操作,這個CPU就是
不停地在運行,呵呵,反正CPU又不是人,所以不需要人權的哈?。?!這種調(diào)度是不是簡
單得不能再簡單了??????。。?!
7.串口不使用中斷,這就是最大可能的降低難度,串口使用論詢的方式來實現(xiàn)讀寫(當
然是阻塞的方式了哦,而且只有寫,不允許讀,因為讀的時候需要涉及到采用中斷方式,
因為輪詢方式有個不好的地方,那就是正在讀的時候,這里有可能當前進程的時間片用
完了,系統(tǒng)切換到另一個進程,這里你在PC機的串口輸入的數(shù)據(jù)就丟棄了,唉,又是為
了簡單嘛)
8,最后一步就是MIAN函數(shù)的最后一部分,將本進程當作IDLE進程(相當于修改任務結構
數(shù)組中的數(shù)據(jù)),開啟中斷,將當前進程加入一段死循環(huán),以免它退出去。
9.編譯你的BOOTLOADER,KERNEL,并燒寫至FLASH,反復調(diào)試
10.至此將你的at91rm9200(或者是其它相類似的芯片)的串口接上PC機,打開超級終端,
打開板子電源,說不定你的操作系統(tǒng)就打印出了"hello,world"了!??!一個最簡單的操作
系統(tǒng)就出來了
下一頁是具體的功能模塊實現(xiàn)
實現(xiàn)一個最簡單的嵌入式操作系統(tǒng)(四)
任務結構數(shù)組(或鏈表)的實現(xiàn)
我們的任務結構就采用鏈表形式吧,但其長度是限定了的,頭指針是一個全局指針變量(
指針變量是一個無符號整型指針,其指針本身所在的地址是在BSS段,但其指向的內(nèi)容是分
配在堆上的一片內(nèi)存),分配內(nèi)核內(nèi)存的函數(shù)就用kmalloc吧,kmalloc函數(shù)需要自己編寫
呵,為了簡單,這個函數(shù)只接受一個參數(shù),就是所需分配大小,這個函數(shù)做得很簡單,首先
有一個全局針指,它在初始化時指向了整個堆的起始位置,并且固定大小,就是所謂的內(nèi)核
堆棧,在內(nèi)核堆棧之后就是用戶堆棧,由于總共有十個任務,當然不包括內(nèi)核本身的任務,
所以整個堆棧就平均分成十一部分,注意:在所有任務初始化完成之后,還有一個步驟就是
將內(nèi)核這個任務移到用戶態(tài),相當于要將自己的任務結構的堆棧指針修改一下就行了),
判斷大小是否超出了內(nèi)核堆的可分配范圍,還有一點,需要維護內(nèi)核堆和其它任務的堆,
需要進行分塊,并且有一個全局的內(nèi)存使用標識,就用數(shù)組吧,簡單,0表示相應的內(nèi)存
部分未占用,1就表示占用,對應的kfree就相當于把標志置0),
對于內(nèi)存的維護,比較復雜,為了簡單,就定為4K,并且不能進行大于四K的內(nèi)存申請,因為
大于4K之后,由于沒有虛擬地址的概念,就不能實現(xiàn)堆上的連續(xù)分配地址,當然在棧上分配
是可以大于4K的,棧是由編譯器和CPU所決定了的
任務結構包括:
1.所剩的時間片
2.本任務所指向的代碼段內(nèi)存地址,這里也就是函數(shù)入口地址
3.本任務所指向的數(shù)據(jù)段地址,這里的數(shù)據(jù)段被包含進了整個內(nèi)核中,所以并沒有用,作為保留
4.本任務的函數(shù)體是否存在,也就是否會被調(diào)度
5.本任務所使用的棧指針
6.本任務所使用的堆指針
7.本任務的標識,用0代表是IDLE,1代表是其它進程
8.所有寄存器的值
9.當前PC值,初始化時被置成了函數(shù)入口地址
首先講解一下任務數(shù)組結構的初始化:
將先定義一個全局指針,然后將此指針強制轉換為一個任務結構指針,并通過kmalloc函在內(nèi)核所
占用的堆(前而講過內(nèi)核的堆的起始就是整個堆的起始)上去分配十個任務結構所占的內(nèi)存,這里
是絕不會超過4K的并且為這十個任務結構賦值,將第一個任務置為IDLE,時間片為20,代碼段內(nèi)存地址為main函數(shù)的的地址,數(shù)據(jù)段地址忽略,函數(shù)體存在,可以被調(diào)度,棧指針指向的位置根據(jù)以下來計算:
假定每個給每個任務可使用的堆棧設定為64K,而整個堆的起始位置是0x20030000,那么第一個堆指針所指向的就是0x20030000,棧就是0x20030000+64K的位置,第二個以后就以此類推
注意:在初始化任務結構之前,不允許系統(tǒng)使用堆,但可以使用棧,那么內(nèi)核任務棧部分就分成了
兩個,在未進行調(diào)度之前,棧就是上一頁中第二步中所設的棧,那么上一頁設置堆棧的時候就得注
意必須將堆棧空間設成十個64K再加上在本步驟使用以前的最大可能所需的??臻g
再講解一下任務切換時所要做的事情:
進入整個中斷處理入口時,會將所有寄存器推入IRQ棧之中,并把值拷貝到當前任務結構相應的字段當中,并取出被中斷的進程的當前PC值存入當前任務結構中的相應字段中,接下就判別中斷類型,以進入相應的中斷處理函數(shù),這里就會進入do_timer函數(shù)中,以下就是進入此函數(shù)之后的流程:
內(nèi)核中還有一個全局指針,就是當前任務指針,它本身也是在系統(tǒng)BSS段中,它的定義如上一步中的那個全局指針一樣,當由系統(tǒng)時鐘中斷之后,就取出這個全局指針,上一步初始化完成之后,還會把這個指針指向第一個任務結構所在位置,也就是0x20030000處,那么就取出這個任務結構中的時間片字段,判斷其是否為0,若為0,就進行以下的操作:保存用戶態(tài)下的棧指針至當前任務結構,保存堆指針,并將搜索一下可以被調(diào)度的任務結構,并將此任務結構賦給當前任務指針,置需要進行任務切換標識,此標識同樣是一個全局變量,但它是被賦了初值,會放在整個系統(tǒng)的DATA段中,返回do_timer函數(shù)。若不為0,就進行以下操作:
將時間片減一,返回do_timer函數(shù)接下來判斷任務切換標識,若為0,則進行以下操作:
不需要進行任務切換,所有寄存器出棧(這里的棧指的是IRQ棧),重新開啟中斷,切換到用戶模式,加載當前任務結構中的當前PC值字段,以退出中斷處理程序若此標識為1,則執(zhí)行以下操作:
就需要進行任務切換,讓所有寄存器出棧(這里的棧指的是IRQ棧),將當前任務結構中的所有寄
存器的值恢復到相應寄存器中,將用戶態(tài)下的棧指針恢復至當前任務結構棧指針,將堆指針恢復至
當前任務結構堆指針,并把需要進行任務切換標識恢復為0,重新開啟中斷,切換到用戶模式,任務切換是通過加載PC值來實現(xiàn)的,也就是通過加載當前任務結構中的當前PC值字段,以退出中斷處理程序
系統(tǒng)調(diào)用的實現(xiàn)
本系統(tǒng)是完全可以不實現(xiàn)系統(tǒng)調(diào)用的,因為沒有實現(xiàn)內(nèi)核態(tài)和用戶態(tài)的保護,完全可以不實現(xiàn)
自己的C庫,所有的函數(shù)都像kmalloc之類的實現(xiàn)一樣,在內(nèi)核中直接寫函數(shù)原型,但為了以后
擴展,還是說一下系統(tǒng)調(diào)用,這里以malloc系統(tǒng)調(diào)用來實現(xiàn)
首先說明還有一個堆指針(前面在kmalloc時有一個堆指針,不過那個堆指針是為內(nèi)核任務,中
斷處理所提供),這里這個堆指針是用于用戶態(tài)的,它在系統(tǒng)初始化完成之前會賦上初值,其初
值就是第一個任務結構所使用的堆的起始位置,也就是在內(nèi)核所使用的堆加上64K的位置
函數(shù)庫中的malloc函數(shù)實現(xiàn)步驟如下:
1.首先檢測申請大小是否超出了4K,若超出4K,就返回錯誤
2.進行系統(tǒng)調(diào)用(這里用_syscall1,并只傳遞一個參數(shù)(所需分配大?。?BR>系統(tǒng)調(diào)用函數(shù)_syscall1的實現(xiàn):
1.將寄存器壓入堆棧(這里的棧指向就是當前任務的棧)
2.將系統(tǒng)調(diào)用號1放至R0,參數(shù)放入R1
3.發(fā)出SWI指令以產(chǎn)生SWI中斷(就是所說的軟中斷,陷阱)
此時系統(tǒng)發(fā)生中斷,會進入SWI中斷處理入口,下面說一下SWI入口函數(shù)的實現(xiàn)
1.取出R0的值,判斷其值,進入相應的分支處理代碼段
2.在此進入_malloc處理代碼段,取出R1的值,然后再得到前面所說的當前堆指針,并申請對應數(shù)
據(jù)塊大小,置用于內(nèi)存占用標識的相應字段,將當前堆指針放入R0,移動當前堆指針,改變當前任
務結構的堆指針,切換到用戶態(tài),返回SWI中斷系統(tǒng)調(diào)用_syscall1的返回處理:
為了簡單,在從內(nèi)核態(tài)返回用戶態(tài)時,不再進行任務的重新調(diào)度,所以上面的步驟就相對簡單
1.當從SWI中斷返回后,系統(tǒng)就運行在了用戶態(tài),此時取出R0的值,并賦值給需要申請內(nèi)存的指針
2.在用戶態(tài)彈出寄存器,返回到上一層函數(shù)
malloc函數(shù)的返回,此時malloc函數(shù)直接返回指針就行了,整個malloc的流程就結束了,其它的系
統(tǒng)調(diào)用同這個過程類似
到此為止,這個操作系統(tǒng)初步實現(xiàn)了,但好像什么事情都不能做,如果讓它支持串口中斷的話,或許可以做那么一點點事情,比如像單片機那樣的功能,整個系統(tǒng)的難點就是中斷處理和任務切換,在本例中,由于ARM不支持像0x86那樣的CPU級的保護模式,所以進行任務切換的時候,就得自己通過加載PC值的方法來實現(xiàn),呵,因為我想不到更好的辦法,但這個辦法有一個不好解決的地方,就是寄存器入棧和出棧的保護,在進入中斷時,必須保護寄存器,但如果需要進行重新調(diào)度,就得從中斷上下文切換到進程上下文中,如何從中斷上下文切換到進程上下文呢??我在這里所采用的方法很笨拙:
1.首先讓寄存器入棧
2.讓寄存器保存至當前任務結構數(shù)組,被中斷掉的進程的PC值保存至任務結構
3.處理timer中斷
4.如果進行任務切換,尋找下一個可調(diào)度的進程,然后把當前任務結構指下剛搜索到
的任務結構,讓寄存器出棧,恢復當前任務結構里的值到寄存器,恢復堆棧指針,切換到用戶態(tài),通過加載當前任務結構的PC值來恢復被掛起的進程這里在中斷上下文中使用了任務結構,這在LINUX上好像是不這樣用的,中斷上下文和進程上下文是兩個不同的概念,中斷上下文中不能訪問進程上下文里的任務結構,我實在想不出有什么辦法來實現(xiàn)進程調(diào)度了,所以請看到我這則文章的人提出好一點的方法
linux相關文章:linux教程
晶振相關文章:晶振原理
評論