第一篇:操作系統實驗報告
許昌學院
《操作系統》實驗報告書
學號:姓名:閆金科班級:成績:
5006140057
14物聯網工程 2016年02月實驗一 Linux的安裝與配置
一、實驗目的
1.熟悉Linux系統的基本概念,比如Linux發行版、宏內核、微內核等。2.掌握Linux系統的安裝和配置過程,初步掌握Linux系統的啟動和退出方法。3.熟悉Linux系統的文件系統結構,了解Linux常用文件夾的作用。
二、實驗內容
1.從網絡上下載VMware軟件和兩個不同Linux發行版鏡像文件。2.安裝VMware虛擬機軟件。
3.在VMware中利用第一個鏡像文件完成第一個Linux的安裝,期間完成網絡信息、用戶信息、文件系統和硬盤分區等配置。
4.在VMware中利用第二個鏡像文件完成第二個Linux的安裝,并通過LILO或者GRUB解決兩個操作系統選擇啟動的問題。
5.啟動Linux系統,打開文件瀏覽器查看Linux系統的文件結構,并列舉出Linux常用目錄的作用。
三、實驗過程及結果
1、啟動VMware,點擊新建Linux虛擬機,如圖所示:
2、點擊下一步,選擇經典型,點擊下一步在選擇客戶機頁面選擇Linux,版本選擇Red Hat Enterprise Linux 5,如圖所示:
3、點擊下一步創建虛擬機名稱以及所要安裝的位置,如圖所示:
4、點擊下一步,磁盤容量填一個合適大小,此處選擇默認值大小10GB,如圖所示:
5、點擊完成,點擊編輯虛擬機設置,選擇硬件選項中的CD-ROM(IDE...)選項,在右側連接中選擇“使用ISO鏡像(I)”選項,點擊“瀏覽”,找到Linux的鏡像文件,如圖所示:
6點擊確定按鈕后,點擊啟動虛擬機按鈕,來到Linux的安裝界面,如圖所示:
7、到此頁面之后,等待自動檢測安裝,如圖所示:
8、等到出現如圖所示頁面后點擊“skip”按鈕,跳過檢測,直接進入安裝設置界面,如圖所示:
9、安裝設計界面如圖所示:
10、點擊Next按鈕進入設置語言界面,設置語言為“簡體中文”,如圖所示:
11、點擊Nest按鈕進入系統鍵盤設置按鈕,設置系統鍵盤為“美國英語式”,如圖所示:
12、點擊下一步按鈕,彈出“安裝號碼”對話框,選擇跳過輸入安裝號碼,如圖所示:
13、按照提示,一直點擊下一步按鈕,如圖所示:
14、到設置最后一步,點擊下一步按鈕進入開始安裝Red Hat Enterprise Linux Sever界面,如圖所示:
15、安裝完成后,進入歡迎界面,按照提示點擊前進按鈕知道進入Linux桌面,如圖所示:
16、安裝成功的Linux系統桌面如圖所示,桌面包含五個圖標,分別為:計算機、jk’s Home、回收站、RHEL/5.3 i386DVD。
四、實驗總結
通過安裝虛擬機等操作讓我認識到Linux這系統一些基本特點,本次試驗學會了安裝虛擬機并且使用虛擬機安裝操作系統,掌握了紅帽Linux系統的安裝和配置過程,以及對鏡像ISO文件的使用,有別于我們機器上使用的系統,通過虛擬機這個軟件還可以在已有系統的基礎上使用其他操作系統。安裝過程中一定要注意選擇版本的時候要選擇Red Hat Enterprise Linux 5版本,否則安裝不能成功。自己動手成功的安裝了Linux系統,自己對Linux的學習產生更大的興趣。
實驗二 Linux操作系統的運行模式
一、實驗目的
1.熟悉Linux系統終端工作環境的使用,了解Linux命令的格式,使用學會利用常用的Linux命令來完成系統的管理和維護。
2.了解X-Windows的特點,熟悉Linux圖形用戶接口的使用,掌握GNOME桌面環境的基本操作。
3.了解和掌握在Linux環境下安裝軟件包的方法,如QQ for Linux等用軟件的安裝方法。
二、實驗內容
1.啟動Linux系統打開虛擬終端界面,使用Linux的在線幫助指令man或help獲得ls、uname、date、cal、mkdir、cp等Linux命令的幫助手冊,了解這些命令的具體使用方法。同時,也可以通過執行“命令名 –help”來顯示該命令的幫助信息,如“ls –help”,試用這些命令。
2.通過uname命令的執行,查看并給出相關系統信息:操作系統的名稱、系統域名、系統CPU名稱等。
3.在主目錄下創建一個名為myetc的子目錄,將/etc目錄下與網絡相關的文件和子目錄拷貝到該目錄,并將這些文件的執行權限設置為可執行。
4.在主目錄/home下創建目錄program、music 和temp,然后在program下建立目錄java和C,列出完成該過程的所有命令。
5.在圖形界面環境中,查看GNOME桌面的面板和桌面,設置GNOME,包括屏幕保護程序、更改背景和指定關聯程序等。6.實現對光盤的加載和訪問,然后卸載。
三、實驗過程及結果
1、打開終端,輸入 【ls –help】來查看【ls】指令的使用方法,同理查看uname、date、cal、mkdir、cp的使用方法。
2、在終端中輸入【uname –a】顯示操作系統名系統cpu名和系統域名
3、重啟系統,用【root】用戶名進入系統,以獲得權限。在終端中輸入【mkdir myetc】,在主目錄下創建【myrtc】的目錄,【ls】查看是否創建。輸入【cd..】返回至【/】文件,輸入【cp –r etc root/myetc】講etc中內容復制到myetc中,進入myetc文件【ls】查看。輸入
【chmod u+x etc】賦予文件可執行的權限,輸入【ll】查看。
4、在home下,輸入【mkdir {program,music,temp}】,可在home下創立這三個目錄,輸入【ls】查看。在program下輸入【mkdir{java,C}】,可創立java和C兩個目錄,【ls】查看。
5、在桌面上方選擇【系統】-【首選項】,即可設置屏幕保護程序和更改背景和指定關聯程序
5、在桌面上可見看到有CD光盤,雙擊瀏覽,右鍵【彈出】即卸載。
四、實驗總結和體會
Linux的指令系統是學習Linux操作系統很重要的一部分,指令系統相當于在Windows操作系統下的doc,可以省去圖形化界面。通過這次的實驗讓我了解了Linux的強大功能,了解到Linux有許多方便快捷的設置基本配置的方法,這使我更喜歡上Linux的使用。在使用指令的過程中,有時候對文件的操作需要一定的權限,這時需要在登陸時用戶名使用【root】,而不是我們在安裝時使用的用戶名,這樣就獲得了管理員權限,可以對一些系統文件進行操作。
實驗三 Linux應用軟件與系統管理
一、實驗目的
1.了解OpenOffice.Org集成辦公軟件,掌握利用OpenOffice.Org的套件來完成文檔和圖片的處理。
2.了解Linux網絡管理的知識,熟悉Linux網絡配置的方法,掌握在Linux環境下配置Web服務器和ftp服務的方法。
二、實驗內容
1.配置Linux系統的網絡環境,安裝FTP和Web服務器,并配置相關的屬性,利用FTP實現WINDOWS和Linux之間的數據交換。
2.利用FTP程序上傳自己的照片到FTP服務器,利用OpenOffice的文字處理工具OpenOffice Writer制作一份表格形式的個人簡歷。個人簡歷中至少包含學號、姓名、性別、專業、照片和學習經歷等內容,并保存為網頁格式(html格式)。3.將個人簡歷網頁設置為WEB服務器的首頁,然后在客戶端利用瀏覽器訪問WEB服務器,查看效果。
4.通過讀取proc文件系統,獲取系統各種信息(如主機名、系統啟動時間、運行時間、版本號、所有進程信息、CPU使用率等),并以比較容易的方式顯示。
三、實驗過程及結果
1.配置網絡環境:在(服務.cmd).里面進行以下操作:在服務里選擇3按回車
完成后,可在本地連接看到VMware已連接上網絡
在虛擬機設置中設置以太網網絡連接方式為
網關地址填虛擬機的網管,IP地址設為虛擬機的一個子網:
四、總結:
在linux系統下,make是我們經常用到的編譯命令,所以關于make代碼和他的操作指令一定要記清楚。所以,熟練掌握了make和makefile工具之后,源碼安裝軟件就變的像windows下安裝軟件一樣簡單。
實驗四 進程控制與管理
一、實驗目的
1.掌握GCC編譯器的用法,學會利用GCC編輯器來編輯C語言程序,學會利用GDB調試器來調試C語言程序。
2.理解進程和程序的區別和聯系,3.掌握在Linux環境下觀察進程運行情況和CPU工作情況的命令。4.了解fork()系統調用,掌握利用fork()創建進程的方法。
5.了解Linux系統其他與進程相關的系統調用,如exec、wait和exit等。6.了解Linux常用的進程通信機制。
二、實驗內容
1.利用Linux的進程管理命令ps、top來監視和跟蹤進程,體會進程和程序的關系。2.利用Linux的文字編輯器編寫文件復制的C語言程序,并用gcc編譯該程序,然后運行該程序。
3.編寫一段程序,使用系統調用fork()創建兩個子進程。當此程序運行時,在系統中有一個父進程和兩個子進程活動。讓每一個進程在屏幕上顯示一個字符:父進程顯示'a',子進程分別顯示字符'b'和字符'c'。試觀察記錄屏幕上的顯示結果,并分析原因。
4.修改上述程序,每一個進程循環顯示一句話。子進程顯示'daughter ?'及'son ??',父進程顯示 'parent ??',觀察結果,分析原因。5.用fork()創建一個進程,再調用exec()用新的程序替換該子進程的內容。
三、實驗過程及結果
1、利用Linux的進程管理命令ps、top來監視和跟蹤進程,體會進程和程序的關系。<1>從用戶身份切換到ROOT身份
<2>輸入命令 ps 查看進程
<2>輸入命令 top 跟蹤進程
2、利用Linux的文字編輯器編寫一個計算機100個自然數和的C語言程序,并用gcc編譯該程序,然后運行該程序。
<1>創建一個.C文件 并進入進行編輯
<2>用GCC 進行編譯,再查看文件,發現產生執行文件 a.out
<3>執行這個可執行文件得到結果5050
1、編寫一段程序,使用系統調用fork()創建兩個子進程。當此程序運行時,在系統中有一個父進程和兩個子進程活動。讓每一個進程在屏幕上顯示一個字符:父進程顯示'a',子進程分別顯示字符'b'和字符'c'。試觀察記錄屏幕上的顯示結果,并分析原因。
<1>穿件一個.C文件 并進行編寫程序代碼
<2>反復執行2次該程序
<3>可以看出兩次執行的結果 a b c 出現的順序不同,原因是,3個進程的輸出次序是隨機的,并不會按規定的順序出現,所以會出現上述結果。
4、修改上述程序,每一個進程循環顯示一句話。子進程顯示'daughter ?'及'son ??',父進程顯示 'parent ??',觀察結果,分析原因。<1>重新修改代碼
<3>執行這段程序
<4>原分析:
因和之前一樣,可以看出執行的結果 3個單詞出現的順序不同,原因是,3個進程的輸出次序是隨機的,并不會按規定的順序出現,所以會出現上述結果。
5、用fork()創建一個進程,再調用exec()用新的程序替換該子進程的內容。<1>
編寫代碼
<2> 執行的結果
結果表明 execl 替代了son的內容
四、實驗總結和體會
這個實驗考察的是進程之間存在很多可能性以及對編輯器的使用。本次實驗學習了在linux環境下用gcc編譯器運行c語言程序,在linux環境下編寫程序用到了vi編輯器,知道了該編輯器也需要各種命令來操作。編寫C語言程序時用到了fork()函數,再調用execl()用新的程序替換該子進程的內容。
實驗五 進程調度模擬程序的設計與實現
一、實驗目的
1.了解進程調度的概念,掌握常用進程調度算法的原理。2.掌握Linux程序設計編輯、編譯和調試的技巧。
二、實驗內容
1.編寫程序實現進程調度調度算法先來先服務、優先級高優先和時間片輪轉調度算法。(編程語言不限)
2.輸入數據,輸出運行結果。
三、實驗過程及結果
1先來先服務
#i nclude
struct { int id;
float ArriveTime;float RequestTime;float StartTime;float EndTime;float RunTime;float DQRunTime;int Status;}arrayTask[4];GetTask(){ int i;float a;
for(i=0;i<4;i++){arrayTask[i].id=i+1;printf(“input the number”);
printf(“input the the ArriveTime of arrayTask[%d]:”,i);scanf(“%f”,&a);
arrayTask[i].ArriveTime=a;
printf(“input the RequestTime of arrayTask[%d]:”,i);scanf(“%f”,&a);
arrayTask[i].RequestTime=a;arrayTask[i].StartTime=0;arrayTask[i].EndTime=0;arrayTask[i].RunTime=0;arrayTask[i].Status=0;
} }
int fcfs()
{
int i,j,w=0;
for(i=0;i<4;i++)
{
if(arrayTask[i].Status==0)
{
t=arrayTask[i].ArriveTime;
w=1;
}
if(w==1)
break;
}
for(i=0;i<4;i++)
{
if(arrayTask[i].ArriveTime t=arrayTask[i].ArriveTime; } for(i=0;i<4;i++) { if(arrayTask[i].ArriveTime==t) return i; } } int sjf(){ int i,x=0,a=0,b=0;float g; for(i=0;i<4;i++){ if(arrayTask[i].Status==1){g=arrayTask[i].EndTime;x=1;} } if(x==0){ t=arrayTask[0].ArriveTime; for(i=0;i<4;i++){ if(arrayTask[i].ArriveTime t=arrayTask[i].ArriveTime;a=i;} } return a;} else { for(i=0;i<4;i++){ if(arrayTask[i].EndTime>g)g=arrayTask[i].EndTime;} for(i=0;i<4;i++){ if(arrayTask[i].Status==0&& arrayTask[i].ArriveTime<=g){ t=arrayTask[i].RequestTime;a=i;b=1;} /*判斷有沒有進程在前個進程完成前到達*/ } if(b!=0)/*有進程到達則按SJF*/ { for(i=0;i<4;i++){ if(arrayTask[i].Status==0&&arrayTask[i].ArriveTime<=g&&arrayTask[i].RequestTime return a;} else{ /*否則按FCFS*/ for(i=0;i<4;i++) {if(arrayTask[i].Status==0)t=arrayTask[i].ArriveTime;} for(i=0;i<4;i++){ if(arrayTask[i].Status==0&&arrayTask[i].ArriveTime return a;} } } new(int s)/*定義執行進程后相關數據的修改*/ { int i,g=0;for(i=0;i<4;i++){ if(arrayTask[i].Status==0)continue;else { g=1;break;} } if(g==0)/*當處理的是第一個未執行的進程時執行*/ { arrayTask[s].StartTime=arrayTask[s].ArriveTime; arrayTask[s].EndTime=arrayTask[s].RequestTime+arrayTask[s].ArriveTime;arrayTask[s].RunTime=arrayTask[s].RequestTime;arrayTask[s].Status=1;g=2;} if(g==1)/*當處理的不是第一個未執行的進程時執行*/ { arrayTask[s].Status=1;for(i=0;i<4;i++){ if(arrayTask[i].Status==1)d=arrayTask[i].EndTime;} for(i=0;i<4;i++)/*查找最后執行的進程的完成時間*/ { if(arrayTask[i].EndTime>d&&arrayTask[i].Status==1)d=arrayTask[i].EndTime;} if(arrayTask[s].ArriveTime arrayTask[s].StartTime=arrayTask[s].ArriveTime; arrayTask[s].EndTime=arrayTask[s].StartTime+arrayTask[s].RequestTime;arrayTask[s].RunTime=arrayTask[s].EndTime-arrayTask[s].ArriveTime;} arrayTask[s].DQRunTime=arrayTask[s].RunTime/arrayTask[s].RequestTime;} Printresult(int j)/*定義打印函數*/ { printf(“%dt”,arrayTask[j].id); printf(“%5.2ft”,arrayTask[j].ArriveTime);printf(“%5.2ft”,arrayTask[j].RequestTime);printf(“%5.2ft”,arrayTask[j].StartTime);printf(“%5.2ft”,arrayTask[j].EndTime);printf(“%5.2ft”,arrayTask[j].RunTime);printf(“%5.2fn”,arrayTask[j].DQRunTime);} main(){ int i,b,k,a,c=0;int d[4];clrscr(); printf(“t F.FCFS n”);printf(“t S.SFJ n”);printf(“t Q.EXIT n”);for(i=0;;i++){ if(c)break; printf(“please input the number a:n”);scanf(“%d”,&a);switch(a){ case Q: c=1;break; case F:printf(“please input the different-ArriveTime of arrayTasksn”);GetTask(); printf(“*****************************the result of fcfsn”);printf(“NumbertArrivetServertStarttFinishtTurnovetTake power turnover timen”); for(b=0;b<4;b++)/*調用兩個函數改變結構體數的值*/ { k=fcfs();d[b]=k;new(k);} for(b=0;b<4;b++) Printresult(d[b]);/*調用打印函數打出結果*/ continue; case S: printf(“please input the different-RequestTime of array Tasksn”);GetTask(); printf(“******************************the result of sjfn”);printf(“NumbertArrivetRequesttStarttEndtRuntDQRun timen”);for(b=0;b<4;b++){ k=sjf();d[b]=k;new(k);} for(b=0;b<4;b++)Printresult(d[b]);continue; default:printf(“the number Error.please input another number!n”);} } } 四、實驗總結和體會 通過做本實驗,讓我對進程或作業先來先服務、高優先權、按時間片輪轉調度算法以及進程調度的概念和算法,有了更深入的認識!理解進程的狀態及變化,動態顯示每個進程的當前狀態及進程的調度情況。進程調度是處理機管理的核心內容。優先級高優先是根據作業的優先級,總是選擇優先級最高者進入隊列。輪轉調度算法是調度程序每次把CPU分配給就緒隊列首進程/線程使用規定的時間間隔,就緒隊列中都路保留巡行一個時間片。 實驗二 進程調度 1.目的和要求 通過這次實驗,理解進程調度的過程,進一步掌握進程狀態的轉變、進程調度的策略,進一步體會多道程序并發執行的特點,并分析具體的調度算法的特點,掌握對系統性能的評價方法。 2.實驗內容 閱讀教材《計算機操作系統》第二章和第三章,掌握進程管理及調度相關概念和原理。 編寫程序模擬實現進程的輪轉法調度過程,模擬程序只對PCB進行相應的調度模擬操作,不需要實際程序。假設初始狀態為:有n個進程處于就緒狀態,有m個進程處于阻塞狀態。采用輪轉法進程調度算法進行調度(調度過程中,假設處于執行狀態的進程不會阻塞),且每過t個時間片系統釋放資源,喚醒處于阻塞隊列隊首的進程。 程序要求如下: 1)輸出系統中進程的調度次序; 2)計算CPU利用率。 3.實驗環境 Windows操作系統、VC++6.0 C語言 4設計思想: (1) 程序中進程可用PCB表示,其類型描述如下: struct PCB_type { int pid; //進程名 int state; //進程狀態 2——表示“執行”狀態 1——表示“就緒”狀態 0——表示“阻塞”狀態 int cpu_time;//運行需要的CPU時間(需運行的時間片個數) } 用PCB來模擬進程; (2)設置兩個隊列,將處于“就緒”狀態的進程PCB掛在隊列ready中;將處于“阻塞”狀態的進程PCB掛在隊列blocked中。隊列類型描述如下: struct QueueNode{ struct PCB_type PCB; Struct QueueNode *next;} 并設全程量: struct QueueNode *ready_head=NULL,//ready隊列隊首指針 *ready_tail=NULL , //ready隊列隊尾指針 *blocked_head=NULL,//blocked隊列隊首指針 *blocked_tail=NULL;//blocked隊列隊尾指針(3)設計子程序: start_state(); 讀入假設的數據,設置系統初始狀態,即初始化就緒隊列和阻塞隊列。 dispath(); 模擬調度,當就緒隊列的隊首進程運行一個時間片后,放到就緒隊列末尾,每次都是隊首進程進行調度,一個進程運行結束就從就緒隊列中刪除,當到t個時間片后,喚醒阻塞隊列隊首進程。 calculate(); 就緒進程運行一次,usecpu加1,當就緒隊列為空時unusecpu加1,CPU利用率為use_cpu/(use_cpu+unuse_cpu)。 5源代碼: #include struct PCB_type { int pid; //進程名 int state; //進程狀態 //2--表示“執行”狀態 //1--表示“就緒”狀態 //0--表示“阻塞”狀態 int cpu_time;//運行需要的CPU時間(需運行的時間片個數)};struct QueueNode{ struct PCB_type PCB; struct QueueNode *next;};struct QueueNode *ready_head=NULL,//ready隊列隊首指針 *ready_tail=NULL,//ready隊列隊尾指針 *block_head=NULL,//blocked隊列隊首指針 *block_tail=NULL; //blocked隊列隊尾指針 int use_cpu,unuse_cpu; void start_state()//讀入假設的數據,設置系統初始狀態 { int n,m; int i; struct QueueNode *p,*q; printf(“輸入就緒節點個數n:”); scanf(“%d”,&n); printf(“輸入阻塞節點個數m:”); scanf(“%d”,&m); p=(struct QueueNode *)malloc(sizeof(struct QueueNode)); p->next =NULL; ready_head=ready_tail=p; for(i=0;i { p=(struct QueueNode *)malloc(sizeof(struct QueueNode)); p->next =NULL; p->PCB.state=1; printf(“輸入就緒進程%d的pid和cpu_time:”,i+1); scanf(“%d%d”,&p->PCB.pid,&p->PCB.cpu_time); ready_tail->next=p; ready_tail=p; } q=(struct QueueNode *)malloc(sizeof(struct QueueNode)); q->next =NULL; block_head=block_tail=q; for(i=0;i { q=(struct QueueNode *)malloc(sizeof(struct QueueNode)); q->next=NULL; q->PCB.state=0; printf(“輸入阻塞進程%d的pid和cpu_time:”,i+1); scanf(“%d%d”,&q->PCB.pid,&q->PCB.cpu_time); block_tail->next=q; block_tail=q; } printf(“n處于就緒狀態的進程有:n”); p=ready_head->next; i=1; while(p) {printf(“進程%d的pid和cpu_time:%5d%5d%5dn“,i,p->PCB.pid,p->PCB.state,p->PCB.cpu_time); p=p->next; i++; } } void dispath() //模擬調度 { int x=0,t; use_cpu=0; unuse_cpu=0; printf(”輸入t:“); scanf(”%d“,&t); printf(”開始調度n“); while(ready_head!=ready_tail||block_head!=block_tail) { struct QueueNode *p,*q; if(ready_head!=ready_tail) { p=ready_head->next; ready_head->next=p->next; p->next=NULL; if(ready_head->next==NULL) { ready_tail=ready_head; } p->PCB.state=2; printf(”進程%d調度t“,p->PCB.pid); state和 use_cpu++; x++; p->PCB.cpu_time--; if(p->PCB.cpu_time) { ready_tail->next=p; ready_tail=p; } else { printf(”進程%d完成t“,p->PCB.pid); free(p); } } else { unuse_cpu++; x++; printf(”空閑一個時間片t“); } if(x==t&&block_head!=block_tail) { q=block_head->next; block_head->next=q->next; q->next=NULL; if(block_head->next==NULL) { block_tail=block_head; } ready_tail->next=q; ready_tail=q; x=0; } } } void calculate() //計算CPU利用率 { printf(”ncpu的利用率%.2fn“,(float)use_cpu/(use_cpu+unuse_cpu)); } void main(){start_state(); dispath(); calculate();} 6運行結果: 7實驗總結: 實驗幫我復習了數據結構和C語言,且鞏固課本知識,知道了如何定義結構體,如何在鏈接隊列中增刪節點。模擬進程調度幫我們鞏固了進程三狀態之間的變遷。懂得調式的重要性。總之,我們明白了理論聯系實際。多看書,多上機。 實驗三 可變分區存儲管理 1.目的和要求 通過這次實驗,加深對內存管理的認識,進一步掌握內存的分配、回收算法的思想。 2.實驗內容 閱讀教材《計算機操作系統》第四章,掌握存儲器管理相關概念和原理。編寫程序模擬實現內存的動態分區法存儲管理。內存空閑區使用自由鏈管理,采用最壞適應算法從自由鏈中尋找空閑區進行分配,內存回收時假定不做與相鄰空閑區的合并。 假定系統的內存共640K,初始狀態為操作系統本身占用64K。在t1時間之后,有作業A、B、C、D分別請求8K、16K、64K、124K的內存空間;在t2時間之后,作業C完成;在t3時間之后,作業E請求50K的內存空間;在t4時間之后,作業D完成。要求編程序分別輸出t1、t2、t3、t4時刻內存的空閑區的狀態。 3.實驗環境 Windows操作系統、VC++6.0 C語言 4.設計思想 模擬內存分配和回收,要設置兩個鏈隊列,一個空閑區鏈和一個占用區鏈,空閑區鏈節點有起始地址,大小和指向下一節點的指針等數據域,占用區鏈節點有起始地址,大小,作業名和指向下一節點的指針等數據域,本實驗用最壞適應算法,每次作業申請內存都是從空閑鏈隊頭節點分配,如果相等,就刪除空閑頭結點,如果小于申請的,就不分配,否則就劃分內存給作業,剩下的內存大小,重新插入空閑鏈隊,按從大到小,接著把作業占用的內存放到占用區鏈節點的末尾。每次作業運行完,就要回收其占用的內存大小,把作業節點按從大到小插入到空閑鏈隊中。5.源代碼: #include struct freelinkNode *next;};struct busylinkNode{ char name; int len;int address;struct busylinkNode *next;};struct freelinkNode *free_head=NULL; //自由鏈隊列(帶頭結點)隊首指針 struct busylinkNode *busy_head=NULL; //占用區隊列隊(帶頭結點)首指針 struct busylinkNode *busy_tail=NULL; //占用區隊列隊尾指針 void start(void)/* 設置系統初始狀態*/ { struct freelinkNode *p; struct busylinkNode *q; free_head=(struct freelinkNode*)malloc(sizeof(struct freelinkNode)); free_head->next=NULL;// 創建自由鏈頭結點 busy_head=busy_tail=(struct busylinkNode*)malloc(sizeof(struct busylinkNode)); busy_head->next=NULL;// 創建占用鏈頭結點 p=(struct freelinkNode *)malloc(sizeof(struct freelinkNode)); p->address=64; p->len=640-64;//OS占用了64K p->next=NULL; free_head->next=p; q=(struct busylinkNode *)malloc(sizeof(struct busylinkNode)); q->name='S';/* S表示操作系統占用 */ q->len=64;q->address=0;q->next=NULL; busy_head->next=q;busy_tail=q;} void requireMemo(char name, int require)/*模擬內存分配*/ { freelinkNode *w,*u,*v;busylinkNode *p;if(free_head->next->len>=require){ p=(struct busylinkNode*)malloc(sizeof(struct busylinkNode)); p->name=name; p->address=free_head->next->address; p->len=require; p->next=NULL; busy_tail->next=p; busy_tail=p;} else printf(”Can't allocate“); w=free_head->next; free_head->next=w->next; if(w->len==require) { free(w);} else { w->address=w->address+require; w->len=w->len-require;} u=free_head; v=free_head->next; while((v!=NULL)&&(v->len>w->len)){ u=v; v=v->next;} u->next=w; w->next=v;} void freeMemo(char name)/* 模擬內存回收*/ { int len; int address;busylinkNode *q,*p;freelinkNode *w,*u,*v;q=busy_head; p=busy_head->next; while((p!=NULL)&&(p->name!=name)) { q=p; p=p->next;} if(p==NULL){ printf(”%c is not exist“,name);} else { if(p==busy_tail) { busy_tail=q; } else { q->next=p->next; len=p->len; address=p->address; free(p); w=(struct freelinkNode*)malloc(sizeof(struct freelinkNode)); w->len=len; w->address=address; u=free_head; v=free_head->next; while((v!=NULL)&&(v->len>len)) { u=v;v=v->next; } u->next=w; w->next=v; } } } void past(int time)/* 模擬系統過了time 時間*/ { printf(”過了時間%d后:n“,time);} void printlink()/* 輸出內存空閑情況(自由鏈的結點)*/ { freelinkNode *p; printf(”內存的空閑情況為:n“); p=(struct freelinkNode *)malloc(sizeof(struct freelinkNode)); p=free_head->next; while(p!=NULL) { printf(”內存的起始地址和內存的大小%5dt%5d:n",p->address,p->len); p=p->next; } } void main(){ int t1=1,t2=2,t3=3,t4=4; start(); past(t1); requireMemo('A',8); requireMemo('B',16); requireMemo('C',64); requireMemo('D',124); printlink(); past(t2); freeMemo('C'); printlink(); past(t3); requireMemo('E',50); printlink(); past(t4); freeMemo('D'); printlink();} 6.運行結果: 7.實驗總結: 鞏固編程能力,和調式能力,復習課本知識,明白理論聯系實際的重要性,動手能力非常重要,多看書,多獨立思考,品味痛苦的過程,享受成功的喜悅。 操作系統實驗報告 院系:數計學院 班級:大類6班 學號:100511624 姓名:明章輝 指導教師:徐軍利 操作 系統 實驗報告 實驗名稱: 線程 控制實驗 計算機科學與技術學院 目錄 一、實驗目的和要求 2 二、實驗內容 2 三、實驗步驟 2 四、實驗結果與分析 3 1.單線程 3 2.單線程(睡眠 4s)3 3.多線程 4 4.多線程(每個子線程睡眠 1s)4 5.單線程與多線程對比 5 五、程序源代碼 5 1.單線程實驗代碼 5 2.單線程實驗代碼 6 六、實驗體會 7 一、實驗目的和要求 通過本實驗掌握在 Linux 操作系統中遵循 Posix線程標準接口進行多線程程序編程,熟練掌握線程的創建 pthread_create(),線程的終止 pthread_exit(),等待線程合并 pthread_join()等線程控制操作,利用信號量或者互斥鎖實現線程建的同步。 二、實驗內容 問題:求 1000000 個浮點數(精確到小數點后 4 位)的平均值(和,最大值,最小值),具體的問題描述流程圖如下圖圖 1 所示: 三、實驗 步驟 1、隨機生成 1000000個浮點數; 2、創建 4個子線程,分別求 250000個浮點數之和; 3、完成 1000000 個浮點數之和并打印結果; 4、統計多線程并發執行完成計算的時間; 5、寫一個單線程程序,同樣完成 1000000 個隨機數求和的計算,統計計算時間,并和前面結果進行對比; 6、讓單線程程序睡眠四秒鐘、多線程程序各子程序睡一秒的條件下(兩個程序總的睡眠時間相同),對比執行結果; 7、分析兩次對比結果的差異,寫出自己的見解。 四、實驗結果與分析 1、單線程完成 1000000 個浮點數的求和運算所用的時間情況如下圖圖 2 所示: 圖 圖 2 單線程計算 時間 分析:實驗中每次隨機產生一個 0 到 1 之間的浮點數,1000000 個這樣的數相加的話的平總和大概就在 500000 左右(按照隨機數的平均值原理),實驗中 sum=,顯然結果正確,整個計算運行時間為。 2、單線程完成 1000000 個浮點數的求和運算,單線程中睡眠 4 秒鐘,所用的時間情況如下圖圖3 所示: 圖 圖 3 單線程計算 時間(睡眠 4 秒)分析:根據上一次單線程的執行情況來看,這一次讓單線程睡眠 4 秒鐘,最后執行時間剛好就是4 秒加上計算時間。也就是說計算 1000000 個浮點數的總和平均時間約為。 3、四個子線程共同完成 1000000 個浮點數的求和計算所用時間情況如下圖圖 4所示: 圖 圖 4 多線程計算時間 分析:因為這次是 4 個子線程并發運行,每個子線程只需計算 250000個浮點數的總和,理想情況下這時候的運行時間應該是這單線程中計算時間的四分之一。從圖中可以看到執行時間是,很顯然這個時間約為單線程求 1000000 個浮點數之和的時間()的四分之一,符合預期的結果。 4、四個子線程共同完成 1000000 個浮點數的求和計算,其中每個子線程睡眠 1 秒鐘,最終所用時間情況如下圖圖 5所示: 圖 圖 5 多線程計算時間(每個 子線程眠 睡眠 1 秒) 分析:這里四個子線程每個子線程睡眠一秒,但由于四個子線程并發同步的在執行,當一個子線程在睡眠時,另外一個子線程卻仍然在繼續求和計算,因此他們一起合作同步完成1000000個浮點數的計算所需的時間就是 1 秒加上上圖中不睡眠的時候的計算時間。從圖中可以 看到≈1s+,所以最終的結果符合預期值。 5、單線程計算時間(睡眠 4s)與多線程計算時間(每個子線程睡眠 1s)對比效果如下圖圖 6 所示: 圖 圖 6 單線程(睡眠 4s)與 與 多線程(每個眠 子線程睡眠 1s)計算時間 對比圖 五、程序源代碼 /************************* *FileName: *Author: *Date:2013/11/22 ***************************/ #include <> #include <> #include <> #include <> #include int i; srand(time(NULL)); for(i=0;i { SUM +=(float)(rand()/(float)RAND_MAX); } sleep(4);} int main(){ pthread_t p; int result; float time; struct timeval start; struct timeval end; gettimeofday(&start,NULL); result=pthread_create(&p,NULL,ADD,NULL); if(result!=0) { printf(“Create Thread of ADD Failuren”); exit(-1); } pthread_join(p,NULL); gettimeofday(&end,NULL); time =((float)-*1000000+(float) -)/1000000; printf(“Signal_Thread_Sum:%.4fn”,SUM); printf(“Signal_Thread_Execution_Time:%.4fs(sleep 4 sec)n”,time); return 0;} /************************* *FileName: *Author:wangtao *Date:2013/11/22 ***************************/ #include <> #include <> #include <> #include <> #include int i; srand(time(NULL)); for(i=0;i { pthread_mutex_lock(&mutex); SUM +=(float)((float)rand()/RAND_MAX); pthread_mutex_unlock(&mutex); } printf(“pthread%d:%.4fn”,*k,SUM); sleep(1);} int main(void){ pthread_t p1,p2,p3,p4; int result1,result2,result3,result4; int k1=1,k2=2,k3=3,k4=4; struct timeval start; struct timeval end; float time; gettimeofday(&start,NULL); pthread_mutex_init(&mutex,NULL); result1=pthread_create(&p1,NULL,(void*)ADD,&k1); result2=pthread_create(&p2,NULL,(void*)ADD,&k2); result3=pthread_create(&p3,NULL,(void*)ADD,&k3); result4=pthread_create(&p4,NULL,(void*)ADD,&k4); if(result1!=0||result2!=0||result3!=0||result4!=0) { printf(“Create Child Thread Failure!n”); exit(1); } pthread_join(p1,NULL); pthread_join(p2,NULL); pthread_join(p3,NULL); pthread_join(p4,NULL); gettimeofday(&end,NULL); time =((float)-*1000000 +(float)-)/1000000; printf(“SUM = %.4fn”,SUM); printf(“Multi_thread_time = %.4fs(Each child thread sleep 1 sec)n”,time); return 0;} 六、實驗體會 這是第一次使用多線程編程編寫代碼,第一次直觀感受到這種多線程編程對程序的執行速率的影響。雖然說操作系統課程已經上了好幾個星期了,課堂上一直在學習多線程編程的算法思想,但是那只是書面上的講授,真正直觀的感受和體會還是得依靠實驗來了解。 因為之前就接觸過 linux 系統,所以對于程序的編譯執行方面還是問題不大,最主要的就是代碼的編寫問題。一開始到實驗室完全不知道要做什么,因為根本連實驗內容都不知道,直到助教在大屏幕上顯示這個實驗題目,我才開始了解實驗的題目和要求。這里我就是想建議一下老師您可以應該給我們實驗題目,讓我們在實驗前就了解一下實驗內容,不然到了實驗室都不知道到底要干嘛。讀懂了解題意之后,我就開始參考所給的一些線程創建函數說明來編寫多線程同步完成1000000個浮點數的求和運算。因為對這些線程函數不是很了解,在使用 pthread_create()函數創建線程時,老是傳入函數參數不對,后來百度之后了解到傳入的子線程函數的類型應該為 void *(*start_thread)(void)形式。最后運行試驗與單線程的進行比較,強烈感受到多線程的并發同步運行的特點。 總之,這次試驗使我對課堂知識有了更深的體會和鞏固,為以后的課程學習打下了基礎。 實驗一 嵌入式開發環境的建立 一、實驗目的 通過此實驗系統,讀者可以了解嵌入式實時操作系統 uC/OS-II 的內核機制和運行原理。本實驗系統展示了 uC/OS-II 各方面的管理功能,包括信號量、隊列、內存、時鐘等。在各個實驗中具體介紹了 uC/OS-II 的相關函數。讀者在做實驗的同時能夠結合理論知識加以分析,了解各個函數的作用和嵌入式應用程序的設計方法,最終對整個 uC/OS-II 和嵌入式操作系統的應用有較為清楚的認識。 二、實驗步驟 1.安裝集成開發環境LambdaEDU 集成開發環境LambdaEDU 的安裝文件夾為 LambdaEDU,其中有一個名為“Setup.exe” 的文件,直接雙擊該文件便可啟動安裝過程。具體的安裝指導請看“LambdaEDU 安裝手 冊.doc”文件。 當 LambdaEDU 安裝完畢之后,我們看到的是一個空的界面,現在就開始一步一步地將 我們的實驗項目建立并運行起來。 2.建立項目 為了我們的實驗運行起來,需要建立1 個項目基于x86 虛擬機的標準應用項目。通過點 擊“文件”、“新建”、“項目”開始根據向導創建一個項目。 在隨后出現的對話框中選擇“Tool/標準應用項目”,點擊下一步,開始創建一個標準的 可執行的應用程序項目。 在隨后出現的對話框中填入項目名稱“ucos_x86_demo”。點擊“下一步”。 選擇“pc386 uC/OS-II 應用(x86)”作為該項目的應用框架。點擊“下一步” 選擇“pc386_elf_tra_debug”作為該項目的基本配置。點擊“完成”。 新創建的項目“ucos_x86_demo”將會被添加到項目列表。src 文件夾下保存了該項目中 包含的源文件。ucos2 文件夾中包含了移植到x86 虛擬機的全部代碼。init.c 文件是基于ucos2 和本虛擬機的一個應用程序。在進行ucos2 內核實驗中,只需要替換init.c 文件,即可。文 件名不限,但是文件名中最好不要使用英文符號和數字以外的其他字符,3.構建項目 到這里,項目配置全部完成。接下來就可以進行構建項目了。 第一次構建本項目,在此項目上點擊右鍵,選擇“重建BSP 及項目”。即可開始構建。 之后彈出的對話框顯示了構建的進度。可以點擊“在后臺運行”,以隱藏該對話框 在構建的同時,在右下角的“構建信息”視圖輸出構建過程中的詳細信息: 注:“重新構建”將本項目中的全部源代碼進行一次完全的編譯和連接,花費時間較多。“構建項目”則僅僅將新修改過的源代碼進行編譯和連接,花費時間最少。“重建BSP及項 目”,不但要完成“重新構建”的全部工作,另外還要編譯與該項目有關的的LambdaEDU 中內置的部分代碼,花費時間最多。但是在項目剛建立后,第一次構建時需要選擇“重建 BSP 及項目”。以后的構建中選擇“重新構建”或“構建項目”即可。另外,在替換了源代 碼中的文件后,需要選擇“重新構建”來完成該項目的構建。 4.配置虛擬機和目標機代理 (1)制作X86啟動盤 在 LambdaEDU 中依次點擊“工具”、“Bochs”、“制作虛擬機啟動映象”。對啟動盤進行一些參數設置后(如下圖所示),系統將自動為你生成一個PC 虛擬機的 啟動盤映像。 (2)配置虛擬機 選擇使用的網絡適配器(網卡)后,點擊“確定”完成配置。 注意:如果計算機上有多網卡,請將其他網卡停用(包括 VMware 虛擬機添加的虛擬 網卡)。 (3)創建目標機代理 配置好虛擬機后,創建目標機代理:點擊LambdaEDU 左下方窗口中綠色的十字符號,在彈出的窗口中選擇“基于TA 的連接方式”,并點擊“下一步”。 在彈出的“新目標機連接配置中”的這些參數,應該與之前制作啟動盤時設置的參數一致。 注意: 名字:輸入目標機的名字(缺省是 default),注意如果和現有目標機重名的話,改個名 字。 連接類型:默認選擇 UDP IP地址:這里輸入目標機(在本實驗系統中是虛擬機)的 IP地址; 最后點擊“確定”,在目標機管理窗口中,可以看到新增加了一個名為default 的目標機 節點 (4)調試應用 啟動虛擬機。 虛擬機啟動后的畫面如下(其中顯示的IP 地址創建虛擬機啟動盤時填入的IP 地址)中設置的IP 地址): 在成功完成構建的項目ucos_x86_demo 中的“pc386_elf_tra_debug”上點擊鼠標右鍵,在彈出的菜單中選擇“調試”,啟動調試器調試生成的程序: 第一次進行調試/運行,需要選擇目標機,如下圖,選擇“Default”,點擊“確定”,開 始向目標機(虛擬機)下載應用程序。程序下載完成后,會彈出一個“確認透視圖切換”對話框,選擇“是”,切換到調試透 視圖。 調試的界面如下: 點擊綠色的按鈕,全速運行。 注意:全速運行后,程序不能夠被暫停和停止。 三、實驗過程中遇到的問題及體會 在設置IP地址時,要求該IP地址與本計算機在同一個子網中,同時要求該 IP地址沒有被網絡上其他計算機使用。此外,通過構建開發環境,處次體驗到了嵌入式開發工作的樂趣。 實驗二 任務的基本管理 一、實驗目的 1.理解任務管理的基本原理,了解任務的各個基本狀態及其變遷過程; 2.掌握 uC/OS-II 中任務管理的基本方法(創建、啟動、掛起、解掛任務); 3.熟練使用 uC/OS-II 任務管理的基本系統調用。 二、實驗原理及程序結構 1.實驗設計 為了展現任務的各種基本狀態及其變遷過程,本實驗設計了 Task0、Task1 兩個任務: 任務 Task0 不斷地掛起自己,再被任務 Task1 解掛,兩個任務不斷地切換執行。通過本實驗,讀者可以清晰地了解到任務在各個時刻的狀態以及狀態變遷的原因。2.運行流程 描述如下: (1)系統經歷一系列的初始化過程后進入 boot_card()函數,在其中調用 ucBsp_init()進 行板級初始化后,調用 main()函數; (2)main()函數調用 OSInit()函數對 uC/OS-II 內核進行初始化,調用 OSTaskCreate 創 建起始任務 TaskStart; (3)main()函數調用函數 OSStart()啟動 uC/OS-II 內核的運行,開始多任務的調度,執 行當前優先級最高的就緒任務 TaskStart;(4)TaskStart 完成如下工作: a、安裝時鐘中斷并初始化時鐘,創建 2 個應用任務; b、掛起自己(不再被其它任務喚醒),系統切換到當前優先級最高的就緒任務Task0。之后整個系統的運行流程如下: ? t1 時刻,Task0 開始執行,它運行到 t2 時刻掛起自己; ? t2 時刻,系統調度處于就緒狀態的優先級最高任務 Task1 執行,它在 t3 時刻喚醒Task0,后者由于優先級較高而搶占 CPU; ? Task0 執行到 t4 時刻又掛起自己,內核調度 Task1 執行; ? Task1 運行至 t5 時刻再度喚醒 Task0; ? …… 3.μC/OS-Ⅱ中的任務描述 一個任務通常是一個無限的循環,由于任務的執行是由操作系統內核調度的,因此任務是絕不會返回的,其返回參數必須定義成 void。在μC/OS-Ⅱ中,當一個運行著的任務使一個比它優先級高的任務進入了就緒態,當前任務的 CPU 使用權就會被搶占,高優先級任務會立刻得到 CPU 的控制權(在系統允許調度和任務切換的前提下)。μC/OS-Ⅱ可以管理多達 64 個任務,但目前版本的μC/OS-Ⅱ有兩個任務已經被系統占用了(即空閑任務和統計任務)。必須給每個任務賦以不同的優先級,任務的優先級號就是任務編號(ID),優先級可以從 0 到 OS_LOWEST_PR10-2。優先級號越低,任務的優先級越高。μC/OS-Ⅱ總是運行進入就緒態的優先級最高的任務。4.源程序說明(1)TaskStart任務 TaskStart 任務負責安裝操作系統的時鐘中斷服務例程、初始化操作系統時鐘,并創建所 有的應用任務: UCOS_CPU_INIT();/* Install uC/OS-II's clock tick ISR */ UCOS_TIMER_START();/*Timer 初始化*/ TaskStartCreateTasks();/* Create all the application tasks */ OSTaskSuspend(OS_PRIO_SELF); 具體負責應用任務創建的 TaskStartCreateTasks 函數代碼如下,它創建了兩個應用任務 Task0 和 Task1: void TaskStartCreateTasks(void){ INT8U i; for(i = 0;i < N_TASKS;i++)// Create tasks { TaskData[i] = i;// Each task will display itsown information } OSTaskCreate(Task0,(void *)&TaskData[0], &TaskStk[0][TASK_STK_SIZE1], 6);} TaskStart 任務完成上述操作后將自己掛起,操作系統將調度當前優先級最高的應用任務Task0 運行。(2)應用任務 應用任務 Task0 運行后將自己掛起,之后操作系統就會調度處于就緒狀態的優先級最高的任務,具體代碼如下: void Task0(void *pdata){ INT8U i;INT8U err;i=*(int *)pdata;for(;;){ printf(“Application tasks switched %d times!nr”,++count); printf(“TASK_0 IS RUNNING..............................................................nr”);printf(“task_1 is suspended!nr”); printf(“**************************************************nr”);err=OSTaskSuspend(5);// suspend itself } } 應用任務 Task1 運行后將 Task0 喚醒,使其進入到就緒隊列中: void Task1(void *pdata){ INT8U i;INT8U err;i=*(int *)pdata;for(;;){ OSTimeDly(150); printf(“Application tasks switched %d times!nr”,++count);printf(“task_0 is suspended!nr”);printf(“TASK_1 IS RUNNING..............................................................nr”);printf(“**************************************************nr”);OSTimeDly(150); err=OSTaskResume(5);/* resume task0 */ } } 三、運行及觀察應用輸出信息 按照本實驗手冊第一部分所描述的方法建立應用項目并完成構建,當我們在 LambdaEDU 調試器的控制下運行構建好的程序后,將看到在μC/OS-Ⅱ內核的調度管理下,兩個應用任務不斷切換執行的情形: 四、本實驗中用到的μC/OS-Ⅱ相關函數 4.1 OSTaskCreate() OSTaskCreate()建立一個新任務。任務的建立可以在多任務環境啟動之前,也可以在 正在運行的任務中建立。中斷處理程序中不能建立任務。一個任務必須為無限循環結構,且 不能有返回點。 OSTaskCreate()是為與先前的μC/OS 版本保持兼容,新增的特性在 OSTaskCreateExt()函數中。 無論用戶程序中是否產生中斷,在初始化任務堆棧時,堆棧的結構必須與 CPU 中斷后 寄存器入棧的順序結構相同。詳細說明請參考所用處理器的手冊。函數原型: INT8U OSTaskCreate(void(*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio); 參數說明: task 是指向任務代碼首地址的指針。 pdata 指向一個數據結構,該結構用來在建立任務時向任務傳遞參數。 ptos 為指向任務堆棧棧頂的指針。任務堆棧用來保存局部變量,函數參數,返回地址 以及任務被中斷時的 CPU 寄存器內容。任務堆棧的大小決定于任務的需要及預計的中斷嵌 套層數。計算堆棧的大小,需要知道任務的局部變量所占的空間,可能產生嵌套調用的函數,及中斷嵌套所需空間。如果初始化常量 OS_STK_GROWTH 設為 1,堆棧被設為從內存高地址 向 低 地 址 增 長,此時 ptos 應 該 指 向任 務堆 棧 空 間 的 最 高 地 址。反 之,如 果OS_STK_GROWTH 設為 0,堆棧將從內存的低地址向高地址增長。prio 為任務的優先級。每個任務必須有一個唯一的優先級作為標識。數字越小,優先級越高。返回值: OSTaskCreate()的返回值為下述之一: ? OS_NO_ERR:函數調用成功。 ? OS_PRIO_EXIST:具有該優先級的任務已經存在。 ? OS_PRIO_INVALID:參數指定的優先級大于 OS_LOWEST_PRIO。? OS_NO_MORE_TCB:系統中沒有 OS_TCB 可以分配給任務了。注意: 任務堆棧必須聲明為 OS_STK 類型。 在任務中必須調用μC/OS 提供的下述過程之一:延時等待、任務掛起、等待事件發生(等待信號量,消息郵箱、消息隊列),以使其他任務得到 CPU。用 戶 程 序 中 不 能 使 用 優 先 級 0,1,2,3,以 及 OS_LOWEST_PRIO-3, OS_LOWEST_PRIO-2, OS_LOWEST_PRIO-1, OS_LOWEST_PRIO。這些優先級μC/OS 系統 保留,其余的 56 個優先級提供給應用程序。4.2 OSTaskSuspend() OSTaskSuspend()無條件掛起一個任務。調用此函數的任務也可以傳遞參數 OS_PRIO_SELF,掛起調用任務本身。當前任務掛起后,只有其他任務才能喚醒。任務掛起 后,系統會重新進行任務調度,運行下一個優先級最高的就緒任務。喚醒掛起任務需要調用 函數 OSTaskResume()。 任務的掛起是可以疊加到其他操作上的。例如,任務被掛起時正在進行延時操作,那么 任務的喚醒就需要兩個條件:延時的結束以及其他任務的喚醒操作。又如,任務被掛起時正 在等待信號量,當任務從信號量的等待對列中清除后也不能立即運行,而必須等到被喚醒后。函數原型: INT8U OSTaskSuspend(INT8U prio);參數說明: prio 為指定要獲取掛起的任務優先級,也可以指定參數 OS_PRIO_SELF,掛起任務本 身。此時,下一個優先級最高的就緒任務將運行。返回值: OSTaskSuspend()的返回值為下述之一: ? OS_NO_ERR:函數調用成功。 ? OS_TASK_ SUSPEND_IDLE:試圖掛起 μC/OS-II 中的空閑任務(Idle task)。此為非法操作。 ? OS_PRIO_INVALID :參數指定的優先級大于 OS_LOWEST_PRIO 或沒有設定 ? OS_PRIO_SELF 的值。 ? OS_TASK_ SUSPEND _PRIO:要掛起的任務不存在。注意: 在程序中 OSTaskSuspend()和 OSTaskResume()應該成對使用。用 OSTaskSuspend()掛起的任務只能用 OSTaskResume()喚醒。4.3 OSTaskResume() OSTaskResume()喚醒一個用 OSTaskSuspend()函數掛起的任務。OSTaskResume()也是唯一能“解掛”掛起任務的函數。函數原型: INT8UOSTaskResume(INT8U prio);參數說明: prio 指定要喚醒任務的優先級。返回值: OSTaskResume()的返回值為下述之一: ? OS_NO_ERR:函數調用成功。 ? OS_TASK_RESUME_PRIO:要喚醒的任務不存在。 ? OS_TASK_NOT_SUSPENDED:要喚醒的任務不在掛起狀態。 ? OS_PRIO_INVALID:參數指定的優先級大于或等于 OS_LOWEST_PRIO。 五、實驗過程中遇到的問題及體會 實驗過程中體會到了嵌入式開發的樂趣,對上課老師所講的內容有了進一步的認識與理解。17 實驗三 信號量:哲學家就餐問題的實現 一、實驗目的 掌握在基于嵌入式實時操作系統 uC/OS-II 的應用中,任務使用信號量的一般原理。通 過經典的哲學家就餐實驗,了解如何利用信號量來對共享資源進行互斥訪問。 二、實驗原理及程序結構 1.實驗設計 掌握在基于嵌入式實時操作系統 uC/OS-II 的應用中,任務使用信號量的一般原理。通 過經典的哲學家就餐實驗,了解如何利用信號量來對共享資源進行互斥訪問。2.源程序說明 五個哲學家任務(ph1、ph2、ph3、ph4、ph5)主要有兩種過程:思考(即睡眠一段時 間)和就餐。每個哲學家任務在就餐前必須申請并獲得一左一右兩支筷子,就餐完畢后釋放 這兩支筷子。五個哲學家圍成一圈,每兩人之間有一支筷子。一共有五支筷子,在該實驗中 用了五個互斥信號量來代表。每個任務的代碼都一樣,如下所示: void Task(void *pdata){ INT8U err;INT8U i;INT8U j; i=*(int *)pdata;j=(i+1)% 5; uC/OS-II 實驗指導書 for(;;){ TaskThinking2Hungry(i);OSSemPend(fork[i], 0, &err); OSSemPend(fork[j], 0, &err);/* Acquire semaphores to eat */ TaskEat(i); OSSemPost(fork[j]); OSSemPost(fork[i]);/* Release semaphore */ OSTimeDly(200);/* Delay 10 clock tick */ } } 操作系統配置 修改 uC_OS-II/OS_CFG.h: :: : #define OS_MAX_EVENTS 10 /*最多可以有 10 個事件*/ #define OS_MAX_FLAGS 5 /*最多可以有 5 個事件標志*/ #define OS_MAX_MEM_PART 5 /*最多可以劃分 5 個內存塊*/ #define OS_MAX_QS 2 /*最多可以使用 2 個隊列*/ #define OS_MAX_TASKS 8 /*最多可以創建 8 個任務*/ #define OS_LOWEST_PRIO 14 /*任務優先級不可以大于 14*/ #define OS_TASK_IDLE_STK_SIZE 1024 /*空閑任務堆棧大小*/ #define OS_TASK_STAT_EN 1 /*是否允許使用統計任務*/ #define OS_TASK_STAT_STK_SIZE 1024 /*統計任務堆棧大小*/ #define OS_FLAG_EN 1 /*是否允許使用事件標志功能*/ #define OS_FLAG_WAIT_CLR_EN 1 /*是否允許等待清除事件標志*/ #define OS_FLAG_ACCEPT_EN 1 /*是否允許使用 OSFlagAccept()*/ #define OS_FLAG_DEL_EN 1 /*是否允許使用 OSFlagDel()*/ #define OS_FLAG_QUERY_EN 1 /*是否允許使用 OSFlagQuery()*/ #define OS_MBOX_EN 0 /*是否允許使用郵箱功能*/ #define OS_MEM_EN 0 /*是否允許使用內存管理的功能*/ #define OS_MUTEX_EN 0 /*是否允許使用互斥信號量的功能*/ #define OS_Q_EN 0 /*是否允許使用隊列功能*/ #define OS_SEM_EN 1 /*是否允許使用信號量功能*/ #define OS_SEM_ACCEPT_EN 1 /*是否允許使用 OSSemAccept()*/ #define OS_SEM_DEL_EN 1 /*是否允許使用OSSemDel()*/ #define OS_SEM_QUERY_EN 1 /*是否允許使用OSSemQuery()*/ #define OS_TASK_CHANGE_PRIO_EN 1 /* 是 否 允 許 使 用 OSTaskChangePrio()*/ #define OS_TASK_CREATE_EN 1 /*是否允許使用 OSTaskCreate()*/ #define OS_TASK_CREATE_EXT_EN 1 /*是否允許使用 OSTaskCreateExt()*/ #define OS_TASK_DEL_EN 1 /*是否允許使用 OSTaskDel()*/ #define OS_TASK_SUSPEND_EN 1 /* 是 否 允 許 使 用 OSTaskSuspend()and OSTaskResume()*/ #define OS_TASK_QUERY_EN 1 /*是否允許使用 OSTaskQuery()*/ #define OS_TIME_DLY_HMSM_EN 1 /* 是 否 允 許 使 用 OSTimeDlyHMSM()*/ #define OS_TIME_DLY_RESUME_EN 1 /* 是 否 允 許 使 用 OSTimeDlyResume()*/ #define OS_TIME_GET_SET_EN 1 /* 是否允許使用 OSTimeGet()和 OSTimeSet()*/ #define OS_SCHED_LOCK_EN 1 /* 是 否 允 許 使 用 OSSchedLock()和 OSSchedUnlock()*/ #define OS_TICKS_PER_SEC 200 /*設置每秒之內的時鐘節拍數目*/ 三、運行及觀察應用輸出信息 開始,所有的哲學家先處于 thinking 狀態,然后都進入 hungry 狀態: 后首先獲得兩個信號量的 1、3 號哲學家開始 eating,待他們釋放相關信號量之后,哲 學家 2、5、4 獲得所需的信號量并 eating: 應用如此這般地循環執行程序下去?? 四、本實驗中用到的μC/OS-Ⅱ相關函數 4.1 OSSemCreate() OSSemCreate()函數建立并初始化一個信號量。信號量的作用如下: ? 允許一個任務和其他任務或者中斷同步 ? 取得設備的使用權 ? 標志事件的發生 函數原型: OS_EVENT *OSSemCreate((((WORD value))))參數說明: value 參數是所建立的信號量的初始值,可以取 0 到 65535 之間的任何值。返回值: OSSemCreate()函數返回指向分配給所建立的信號量的控制塊的指針。如果沒有可用的 控制塊,OSSemCreate()函數返回空指針。注意: 必須先建立信號量,然后使用。4.2 OSSemPend() OSSemPend()函數用于任務試圖取得設備的使用權,任務需要和其他任務或中斷同 步,任務需要等待特定事件的發生的場合。如果任務調用 OSSemPend()函數時,信號量 的值大于零,OSSemPend()函數遞減該值并返回該值。如果調用時信號量等于零,OSSemPend()函數函數將任務加入該信號量的等待隊列。OSSemPend()函數掛起當前 任務直到其他的任務或中斷置起信號量或超出等待的預期時間。如果在預期的時鐘節拍內信 號量被置起,μC/OS-Ⅱ默認最高優先級的任務取得信號量恢復執行。一個被 OSTaskSuspend()函數掛起的任務也可以接受信號量,但這個任務將一直保持掛起狀態直到通過調用 OSTaskResume()函數恢復任務的運行。函數原型: :: : Void OSSemPend(OS_EVNNT *pevent, INT16U timeout, int8u *err);參數說明: :: : pevent 是指向信號量的指針。該指針的值在建立該信號量時可以得到。(參考 OSSemCreate()函數)。 Timeout 允許一個任務在經過了指定數目的時鐘節拍后還沒有得到需要的信號量時 恢復就緒狀態。如果該值為零表示任務將持續地等待信號量,最大的等待時間為 65535 個時 鐘節拍。這個時間長度并不是非常嚴格的,可能存在一個時鐘節拍的誤差。 Err 是指向包含錯誤碼的變量的指針。OSSemPend()函數返回的錯誤碼可能為下述幾 種: ? OS_NO_ERR :信號量不為零。 ? OS_TIMEOUT :信號量沒有在指定數目的時鐘周期內被設置。 ? OS_ERR_PEND_ISR :從中斷調用該函數。雖然規定了不允許從中斷調用該函數,但 μC/OS-Ⅱ仍然包含了檢測這種情況的功能。 ? OS_ERR_EVENT_TYPE :pevent 不是指向信號量的指針。返回值: 無 注意: 必須先建立信號量,然后使用。不允許從中斷調用該函數。 4.3 OSSemPost() OSSemPost()函數置起指定的信號量。如果指定的信號量是零或大于零,OSSemPost()函數遞增該信號量并返回。如果有任何任務在等待信號量,最高優先級的任務將得到信 號量并進入就緒狀態。任務調度函數將進行任務調度,決定當前運行的任務是否仍然為最高 優先級的就緒狀態的任務。函數原型: INT8U OSSemPost(OS_EVENT *pevent);參數說明: pevent 是指向信號量的指針。該指針的值在建立該信號量時可以得到。(參考 OSSemCreate()函數)。返回值: OSSemPost()函數的返回值為下述之一: ? OS_NO_ERR :信號量被成功地設置 ? OS_SEM_OVF :信號量的值溢出 ? OS_ERR_EVENT_TYPE :pevent 不是指向信號量的指針 注意: 必須先建立信號量,然后使用。4.4 OSTimeDly() OSTimeDly()將一個任務延時若干個時鐘節拍。如果延時時間大于 0,系統將立即進 行任務調度。延時時間的長度可從 0 到 65535 個時鐘節拍。延時時間 0 表示不進行延時,函 數將立即返回調用者。延時的具體時間依賴于系統每秒鐘有多少時鐘節拍(由文件 SO_CFG.H 中的常量 OS_TICKS_PER_SEC 設定)。函數原型: void OSTimeDly(INT16U ticks);參數說明: ticks 為要延時的時鐘節拍數。返回值: 無 注意: 注意到延時時間 0 表示不進行延時操作,而立即返回調用者。為了確保設定的延時時間,建議用戶設定的時鐘節拍數加 1。例如,希望延時 10 個時鐘節拍,可設定參數為 11。 五、實驗過程中遇到的問題及體會 在實驗前要對該問題進行深入的理解,即五個哲學家任務(ph1、ph2、ph3、ph4、ph5)主要有兩種過程:思考(即睡眠一段時間)和就餐。每個哲學家任務在就餐前必須申請并獲得一左一右兩支筷子,就餐完畢后釋放這兩支筷子。五個哲學家圍成一圈,每兩人之間有一支筷子。只有理解了,才能更好的進行實驗。 計算機學院實驗報告 課 程實驗名稱 專 業班 級學 號學生姓名 操作系統 進程的控制 計算機操作系統 131110196 鄒明鎮 嘉應學院計算機學院 1305 一、實驗目的 熟悉進程的睡眠、同步、撤消等進程控制方法 ? 利用 wait()來控制進程執行順序 ? 二、實驗原理 1.sleep()使當前的進程睡眠,即當前的進程進入阻塞態。2.wait()等待子進程運行結束。如果子進程沒有完成,父進程一直等待。wait()將調用進程掛起,直至其子進程因暫停或終止而發來軟中斷信號為止。如果在wait()前已有子進程暫停或終止,則調用進程做適當處理后便返回。系統調用格式: int wait(int *status);其中,status是用戶空間的地址。它的低8位反應子進程狀態,為0表示子進程正常結束,非0則表示出現了各種各樣的問題;高8位則帶回了exit()的返回值。exit()返回值由系統給出。 核心對 wait()作以下處理: (1)首先查找調用進程是否有子進程,若無,則返回出錯碼; (2)若找到一處于“僵死狀態”的子進程,則將子進程的執行時間加到父進程的執行時間上,并釋放子進程的進程表項; (3)若未找到處于“僵死狀態”的子進程,則調用進程便在可被中斷的優先級上睡眠,等待其子進程發來軟中斷信號時被喚醒。3.exit()終止進程的執行。系統調用格式: void exit(int status);其中,status是返回給父進程的一個整數,以備查考。 為了及時回收進程所占用的資源并減少父進程的干預,UNIX/LINUX利用exit()來實現進程的自我終止,通常父進程在創建子進程時,應在進程的末尾安排一條exit(),使子進程自我終止。exit(0)表示進程正常終止,exit(1)表示進程運行有錯,異常終止。 如果調用進程在執行exit()時,其父進程正在等待它的終止,則父進程可立即得到其返回的整數。核心須為exit()完成以下操作:(1)關閉軟中斷(2)回收資源(3)寫記帳信息 (4)置進程為“僵死狀態” /* 父進程 */ int status;wait(&status);/*同步*/ printf(“Child process completed: %dn”, status);} else { /* 子進程 */ printf(“Hello child!n”);return 0;} } 思考:這個程序中,子進程的退出狀態值是多少? 5)利用 exit()設定子進程的退出狀態 #include main(){ int pid = fork();/*創建子進程*/ if(pid > 0){ /* 父進程 */ int status;wait(&status);/*同步*/ printf(“Child process completed: %d(%d, %d)n”, status, status/256, status%256);} else { /* 子進程 */ printf(“Hello child!n”);exit(2);} } 思考:這個程序中,子進程的退出狀態值是多少?如何獲得其低8位和高8位? 思考與練習: 試回答每個程序后面的思考題。 四、實驗環境 本次實驗所使用的系統平臺Linux(Ubuntu)和相關軟件GCC。 3)同步 5)利用 exit()設定子進程的退出狀態第二篇:操作系統實驗報告
第三篇:操作系統第一次實驗報告
第四篇:嵌入式操作系統實驗報告
第五篇:操作系統實驗報告