51簡(jiǎn)單操作系統(tǒng)OS_第1頁
51簡(jiǎn)單操作系統(tǒng)OS_第2頁
51簡(jiǎn)單操作系統(tǒng)OS_第3頁
51簡(jiǎn)單操作系統(tǒng)OS_第4頁
全文預(yù)覽已結(jié)束

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、當(dāng)然,這里要申明一下,這玩意兒其實(shí)算不上真正的操作系統(tǒng),它除了并行多任務(wù)并行外根本沒有別的功能.但凡事都從簡(jiǎn)單開始,搞懂了它,就能根據(jù)應(yīng)用需求,將它擴(kuò)展成一個(gè)真正的操作系統(tǒng).好了,代碼來了.將下面的代碼直接放到KEIL里編譯,在每個(gè)task?()函數(shù)的"task_switch();"那里打上斷點(diǎn),就可以看到它們的確是"同時(shí)"在執(zhí)行的.#include<reg51.h>#defineMAX_TASKS2/任務(wù)槽個(gè)數(shù).必須和實(shí)際任務(wù)數(shù)一至#defineMAX_TASK_DEP12/最大棧深.最低不得少于2個(gè),保守值為12.unsignedchar

2、idatatask_stackMAX_TASKSMAX_TASK_DEP;/任務(wù)堆棧.unsignedchartask_id;/當(dāng)前活動(dòng)任務(wù)號(hào)/任務(wù)切換函數(shù)(任務(wù)調(diào)度器)voidtask_switch()task_sptask_id=SP;if(+task_id=MAX_TASKS)task_id=0;SP=task_sptask_id;/任務(wù)裝入函數(shù).將指定的函數(shù)(參數(shù)1)裝入指定(參數(shù)2)的任務(wù)槽中.如果該槽中原來就有任務(wù),則原任務(wù)丟失但系統(tǒng)本身不會(huì)發(fā)生錯(cuò)誤.voidtask_load(unsignedintfn,unsignedchartid)task_sptid=task_stackt

3、id+1;task_stacktid0=(unsignedint)fn&0xff;task_stacktid1=(unsignedint)fn>>8;/從指定的任務(wù)開始運(yùn)行任務(wù)調(diào)度.調(diào)用該宏后,將永不返回.#defineos_start(tid)task_id=tid,SP=task_sptid;return;/*=以下為測(cè)試代碼=*/voidtask1()staticunsignedchari;while(1)i+;task_switch();/編譯后在這里打上斷點(diǎn)voidtask2()staticunsignedcharj;while(1)j+=2;task_switc

4、h();/編譯后在這里打上斷點(diǎn)voidmain()/這里裝載了兩個(gè)任務(wù),因此在定義MAX_TASKS時(shí)也必須定義為2task_load(task1,0);/將task1函數(shù)裝入0號(hào)槽task_load(task2,1);/將task2函數(shù)裝入1號(hào)槽os_start(0);限于篇幅我已經(jīng)將代碼作了簡(jiǎn)化,并刪掉了大部分注釋,大家可以直接下載源碼包,里面完整的注解,并帶KEIL工程文件,斷點(diǎn)也打好了,直接按ctrl+f5就行了.現(xiàn)在來看看這個(gè)多任務(wù)系統(tǒng)的原理:這個(gè)多任務(wù)系統(tǒng)準(zhǔn)確來說,叫作"協(xié)同式多任務(wù)".所謂"協(xié)同式",指的是當(dāng)一個(gè)任務(wù)持續(xù)運(yùn)行而不釋放資源時(shí),

5、其它任務(wù)是沒有任何機(jī)會(huì)和方式獲得運(yùn)行機(jī)會(huì),除非該任務(wù)主動(dòng)釋放CPU.在本例里,釋放CPU是靠task_switch()來完成的.task_switch()函數(shù)是一個(gè)很特殊的函數(shù),我們可以稱它為"任務(wù)切換器".要清楚任務(wù)是如何切換的,首先要回顧一下堆棧的相關(guān)知識(shí).有個(gè)很簡(jiǎn)單的問題,因?yàn)樗?jiǎn)單了,所以相信大家都沒留意過:我們知道,不論是CALL還是JMP,都是將當(dāng)前的程序流打斷,請(qǐng)問CALL和JMP的區(qū)別是什么?你會(huì)說:CALL可以RET,JMP不行.沒錯(cuò),但原因是啥呢?為啥CALL過去的就可以用RET跳回來,JMP過去的就不能用RET來跳回呢?很顯然,CALL通過某種方法保

6、存了打斷前的某些信息,而在返回?cái)帱c(diǎn)前執(zhí)行的RET指令,就是用于取回這些信息.不用多說,大家都知道,"某些信息"就是PC指針,而"某種方法"就是壓棧.很幸運(yùn),在51里,堆棧及堆棧指針都是可被任意修改的,只要你不怕死.那么假如在執(zhí)行RET前將堆棧修改一下會(huì)如何?往下看:當(dāng)程序執(zhí)行CALL后,在子程序里將堆棧剛才壓入的斷點(diǎn)地址清除掉,并將一個(gè)函數(shù)的地址壓入,那么執(zhí)行完RET后,程序就跳到這個(gè)函數(shù)去了.事實(shí)上,只要我們?cè)赗ET前將堆棧改掉,就能將程序跳到任務(wù)地方去,而不限于CALL里壓入的地址.重點(diǎn)來了首先我們得為每個(gè)任務(wù)單獨(dú)開一塊內(nèi)存,這塊內(nèi)存專用于作為對(duì)應(yīng)的

7、任務(wù)的堆棧,想將CPU交給哪個(gè)任務(wù)只需將棧指針指向誰內(nèi)存塊就行了.接下來我們構(gòu)造一個(gè)這樣的函數(shù):當(dāng)任務(wù)調(diào)用該函數(shù)時(shí),將當(dāng)前的堆棧指針保存一個(gè)變量里,并換上另一個(gè)任務(wù)的堆棧指針.這就是任務(wù)調(diào)度器了.OK了,現(xiàn)在我們只要正確的填充好這幾個(gè)堆棧的原始內(nèi)容,再調(diào)用這個(gè)函數(shù),這個(gè)任務(wù)調(diào)度就能運(yùn)行起來了.那么這幾個(gè)堆棧里的原始內(nèi)容是哪里來的呢?這就是"任務(wù)裝載"函數(shù)要干的事了.在啟動(dòng)任務(wù)調(diào)度前將各個(gè)任務(wù)函數(shù)的入口地址放在上面所說的"任務(wù)專用的內(nèi)存塊"里就行了!對(duì)了,順便說一下,這個(gè)"任務(wù)專用的內(nèi)存塊"叫作"私棧",私棧的意思就

8、是說,每個(gè)任務(wù)的堆棧都是私有的,每個(gè)任務(wù)都有一個(gè)自已的堆棧.話都說到這份上了,相信大家也明白要怎么做了:1. 分配若干個(gè)內(nèi)存塊,每個(gè)內(nèi)存塊為若干字節(jié):這里所說的"若干個(gè)內(nèi)存塊"就是私棧,要想同時(shí)運(yùn)行幾少個(gè)任務(wù)就得分配多少塊.而"每個(gè)子內(nèi)存塊若干字節(jié)就是棧深.記住,每調(diào)一層子程序需要2字節(jié).如果不考慮中斷,4層調(diào)用深度,也就是8字節(jié)棧深應(yīng)該差不多了unsignedcharidatatask_stackMAX_TASKSMAX_TASK_DEP當(dāng)然,還有件事不能忘,就是堆指針的保存處.不然光有堆棧怎么知道應(yīng)該從哪個(gè)地址取數(shù)據(jù)啊unsignedcharidatatask

9、_spMAX_TASKS上面兩項(xiàng)用于裝任務(wù)信息的區(qū)域,我們給它個(gè)概念叫"任務(wù)槽".有些人叫它"任務(wù)堆",我覺得還是"槽"比較直觀對(duì)了,還有任務(wù)號(hào).不然怎么知道當(dāng)前運(yùn)行的是哪個(gè)任務(wù)呢?unsignedchartask_id當(dāng)前運(yùn)行存放在1號(hào)槽的任務(wù)時(shí),這個(gè)值就是1,運(yùn)行2號(hào)槽的任務(wù)時(shí),這個(gè)值就是22. 構(gòu)造任務(wù)調(diào)度函函數(shù):voidtask_switch()task_sptask_id=SP;/保存當(dāng)前任務(wù)的棧指針if(+task_id=MAX_TASKS)/任務(wù)號(hào)切換到下一個(gè)任務(wù)task_id=0;SP=task_sptask_id;/

10、將系統(tǒng)的棧指針指向下個(gè)任務(wù)的私棧.3. 裝載任務(wù):將各任務(wù)的函數(shù)地址的低字節(jié)和高字節(jié)分別入在task_stack任務(wù)號(hào)0和task_stack任務(wù)號(hào)1中:為了便于使用,寫一個(gè)函數(shù):task_load(函數(shù)名,任務(wù)號(hào))voidtask_load(unsignedintfn,unsignedchartid)task_sptid=task_stacktid+1;task_stacktid0=(unsignedint)fn&0xff;task_stacktid1=(unsignedint)fn>>8;4. 啟動(dòng)任務(wù)調(diào)度器:將棧指針指向任意一個(gè)任務(wù)的私棧,執(zhí)行RET指令.注意,這可很

11、有學(xué)問的哦,沒玩過堆棧的人腦子有點(diǎn)轉(zhuǎn)不彎:這一RET,RET到哪去了?嘿嘿,別忘了在RET前已經(jīng)將堆棧指針指向一個(gè)函數(shù)的入口了.你別把RET看成RET,你把它看成是另一種類型的JMP就好理解了.SP=task_sp任務(wù)號(hào);return;做完這4件事后,任務(wù)"并行"執(zhí)行就開始了.你可以象寫普通函數(shù)一個(gè)寫任務(wù)函數(shù),只需(目前可以這么說)注意在適當(dāng)?shù)臅r(shí)候(例如以前調(diào)延時(shí)的地方)調(diào)用一下task_switch(),以讓出CPU控制權(quán)給別的任務(wù)就行了.最后說下效率問題.這個(gè)多任務(wù)系統(tǒng)的開銷是每次切換消耗20個(gè)機(jī)器周期(CALL和RET都算在內(nèi)了),貴嗎?不算貴,對(duì)于很多用狀態(tài)機(jī)方式實(shí)現(xiàn)的多任務(wù)系統(tǒng)來說,其實(shí)效率還沒這么高-caseswitch和if()可不像你想像中那么便宜.關(guān)于內(nèi)存的消耗我要說

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論