操作系統實驗指導_第1頁
操作系統實驗指導_第2頁
操作系統實驗指導_第3頁
操作系統實驗指導_第4頁
操作系統實驗指導_第5頁
已閱讀5頁,還剩46頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

PAGEPAGE50《計算機操作系統》實驗指導蘇州科技學院電子與信息工程學院計算機工程系二O一四年九月

目錄Linux平臺TOC\o"1-3"\h\z實驗一命令解釋程序 2實驗二進程管理 3實驗三進程間通信 5實驗四存儲管理 14實驗五設備管理 22實驗六軟盤I/O 32實驗七文件系統 41Windows平臺實驗一進程同步 42實驗二內存管理實驗 44實驗三快速文件系統實習 46實驗四進程之間通信 47實驗五Windows應用程序與動態鏈接庫 49實驗六WDM驅動程序開發 50

實驗一命令解釋程序實驗名稱:命令解釋程序 實驗項目性質:設計性所涉及課程:操作系統 計劃學時:2承擔實驗室:計算機實驗室 實驗環境要求:RedhatLinux適用專業:計算機科學與技術一、實驗目的 1、通過本實驗熟悉UNIX/LINUX操作系統及C語言。 2、熟悉系統調用的編程方法。二、實驗預備內容 查閱實驗中相關函數調用的用法(可用man命令): gets, strcspn, strncpy, strcmp, system等三、實驗內容 利用C語言編寫一個微型命令解釋程序minishell.c,該程序可接收并解釋以下命令: (1) dir 列出當前目錄 (2) cop file1 file2 拷貝文件 (3) era filename 刪除文件 (4) disp string 顯示字符串 (5) end 結束,退出要求: (1)檢查命令的合法性,如果有錯誤,顯示出錯信息,等待重新輸入; (2)命令前后有空格示為合法命令。四、示例程序minishell.c//文件名 minishell.cpp//功能 小型SHELL命令解釋程序//開發環境 #definetrue 1#defineflase 0#include<stdio.h>#include<string.h>#include<stdlib.h>voidmain(){ charcmdl[80];char*scwt[]={"exit","dir","time"}; staticintcmdnum=3; //可用的命令數 charcmd[80]; intj,n; while(true) { printf("Pleaseinputcommand:"); gets(cmdl); //取命令行輸入 n=strcspn(cmdl,""); //取命令命令部分 if(n>0||strlen(cmdl)>0) {strnexicpy(cmd,cmdl,n); cmd[n]='\0'; for(j=0;j<cmdnum;j++) if(strcmp(cmd,scwt[j])==0) break; if(j==0) //是exit命令? exit(0); if(j<cmdnum) //其他合法命令 { system(cmdl); continue; } printf("Badcommand!\n");//命令錯 } }}實驗二進程管理實驗名稱:進程管理 實驗項目性質:驗證性所涉及課程:操作系統 計劃學時:2承擔實驗室:計算機實驗室 實驗環境要求:RedhatLinux適用專業:計算機科學與技術一、實驗目的 1、加深對進程概念的理解,明確進程和程序的區別。 2、進一步認識并發執行的實質。 3、分析進程競爭資源的現象,學習解決進程互斥的方法。二、實驗預備內容 1、閱讀Linux的sched.h源代碼文件,加深對進程管理概念的理解。 2、閱讀Linux的fork.c源代碼文件,分析進程的創建過程。 3、查閱系統調用fork(),lockf(),exit(),wait(),sleep()等的用法。三、實驗內容 1、進程的創建 編寫一段程序,使用系統調用fork()創建兩個子進程。讓父進程顯示字符串‘Parent:’;兩個子進程分別顯示字符串‘Child1:’和‘Child2:’。多次運行此程序,觀察屏幕顯示的結果,并分析原因。 2、進程控制 修改已編寫的程序,將輸出多次重復的一句話,觀察程序執行時在屏幕上顯示的結果,并分析原因。若在程序中使用系統調用lockf()來給每一個進程加鎖,可以實現進程之間的互斥,觀察屏幕顯示的結果,并分析原因。四、函數調用示例1、系統調用fork()原型: #include<sys/types.h> #include<unistd.h> pid_tfork(void);返回值:子進程中為0,父進程中為子進程ID,出錯為-1 由fork創建的新進程被稱為子進程(childprocess)。該函數被調用一次,但返回兩次。兩次返回的區別是子進程的返回值是0,而父進程的返回值則是新子進程的進程ID。將子進程ID返回給父進程的理由是:因為一個進程的子進程可以多于一個,所以沒有一個函數使一個進程可以獲得其所有子進程的進程ID。fork使子進程得到返回值0的理由是:一個進程只會有一個父進程,所以子進程總是可以調用getppid以獲得其父進程的進程ID(進程ID0總是由交換進程使用,所以一個子進程的進程ID不可能為0)。 子進程和父進程繼續執行fork之后的指令。子進程是父進程的復制品。例如,子進程獲得父進程數據空間、堆和棧的復制品。注意,這是子進程所擁有的拷貝。父、子進程并不共享這些存儲空間部分。如果正文段是只讀的,則父、子進程共享正文段。2、fork()調用示例#include<stdio.h>main(){ intp1,p2; while((p1=fork())==-1); if(p1==0) //是子進程? putchar('b'); else //父進程 { putchar('a');}}實驗三進程間通信實驗名稱:進程間通信 實驗項目性質:綜合性所涉及課程:操作系統 計劃學時:4承擔實驗室:計算機實驗室 實驗環境要求:RedhatLinux適用專業:計算機科學與技術一、實驗目的 1、了解和熟悉Linux支持的消息通信機制、管道道通信、共享存儲區機制及信息量機制。2、掌握利用Linux系統的進程通信機制(IPC)實現進程間交換數據的方法。二、實驗預備內容1、閱讀Linux系統的msg.c、sem.c和shm.c等源碼文件,熟悉Linux的三種通信機制。2、查閱系統調用pipe(),write(),read(),exit(),wait(),sleep()等的用法。三、實驗內容 1、進程通信 編寫一段程序,實現進程的管道通信。 使用系統調用pipe()建立一條管道線:兩個子進程P1和P2分別向管道各寫一句話: Child1issendingamessage! Child2issendingamessage!父進程則從管道中讀出來自兩個了進程的信息,顯示在屏幕上。要求父進程先接收子進程P1發來的消息,然后再接收子進程P2發來的消息。(可以通過sleep()將自身進入睡眠)2、消息的創建,發送和接收. (1)使用系統調用msgget(),msgsnd(),msgsrv()及msgctl()編制一長度為1K的消息(如個人通信錄信息)的發送和接收程序.①觀察上面程序,說明控制消息隊列系統調用msgctl()②共享存儲區的發送和接收。(2)使用共享存儲區相關的系統調用shmget(),shmat(),sgmdt(),shmctl(),編制一個與上述功能相同的程序.(3)比較上述兩種消息通信機制中數據傳輸的時間。四、函數調用示例 1、管道1)系統調用pipe() 原型:intpipe(intfd[2]); 返回值:如果系統調用成功,返回0 如果系統調用失敗返回-1:errno=EMFILE(沒有空閑的文件描述符) EMFILE(系統文件表已滿) EFAULT(fd數組無效) 參數是一個包括兩個整數的數組。如果系統調用成功,此數組將包括管道使用的兩個文件描述符。創建一個管道之后,一般情況下進程將產生一個新的進程。注意fd[0]用于讀取管道,fd[1]用于寫入管道。 一旦創建了管道,就可以創建一個新的子進程: 2)pipe()調用示例#include<stdio.h>main(){intid,fd[2];charbuf[50],s[50];pipe(fd);while((id=fork())==-1);if(id==0){ sprintf(buf,"Childissendingmessage!"); write(fd[1],buf,50); exit(0); }else{ wait(0); read(fd[0],s,50); printf("%s\n",s); exit(0); }}2、共享內存相關1)系統調用:shmget();原型:intshmget(key_tkey,intsize,intshmflg);返回值:如果成功,返回共享內存段標識符。如果失敗,則返回-1:errno=EINVAL(無效的內存段大小) EEXIST(內存段已經存在,無法創建) EIDRM(內存段已經被刪除) ENOENT(內存段不存在) EACCES(權限不夠) ENOMEM(沒有足夠的內存來創建內存段) 系統調用shmget()中的第一個參數是關鍵字值(它是用系統調用ftok()返回的)。其他的操作都要依據shmflg中的命令進行。 IPC_CREAT如果系統內核中沒有共享的內存段,則創建一個共享的內存段。 IPC_EXCL當和IPC_CREAT一同使用時,如果共享內存段已經存在,則調用失敗。 當IPC_CREAT單獨使用時,系統調用shmget()要么返回一個新創建的共享內存段的標識符,要么返回一個已經存在的共享內存段的關鍵字值。如果IPC_EXCL和IPC_CREAT一同使用,則要么系統調用新創建一個共享的內存段,要么返回一個錯誤值-1。IPC_EXCL單獨使用沒有意義。2)系統調用shmat()原型:intshmat(intshmid,char*shmaddr,intshmflg);返回值:如果成功,則返回共享內存段連接到進程中的地址。 如果失敗,則返回-1:errno=EINVAL(無效的IPCID值或者無效的地址) ENOMEM(沒有足夠的內存) EACCES(存取權限不夠) 如果參數addr的值為0,那么系統內核則試圖找出一個沒有映射的內存區域。推薦使用這種方法。可指定一個地址,但這通常是為了加快對硬件設備的存取,或者解決和其他程序的沖突。 3)系統調用:shmctl();原型:intshmctl(intshmqid,intcmd,structshmid_ds*buf);返回值:0,如果成功。 -1,如果失敗:errno=EACCES(沒有讀的權限,同時命令是IPC_STAT) EFAULT(buf指向的地址無效,同時命令是IPC_SET和IPC_STAT) EIDRM(內存段被移走) EINVAL(shmqid無效) EPERM(使用IPC_SET或者IPC_RMID命令,但調用進程沒有寫的權限)參數cmd操作命令: IPC_STAT 讀取一個內存段的數據結構shmid_ds,并將它存儲在buf參數指向的地址中。 IPC_SET 設置內存段的數據結構shmid_ds中的元素ipc_perm的值。從參數buf中得到要設置的值。 IPC_RMID 標志內存段為移走。 命令IPC_RMID并不真正從系統內核中移走共享的內存段,而是把內存段標記為可移除。進程調用系統調用shmdt()脫離一個共享的內存段。4)系統調用:shmdt();調用原型:intshmdt(char*shmaddr);返回值:如果失敗,則返回-1:errno=EINVAL(無效的連接地址) 當一個進程不在需要共享的內存段時,它將會把內存段從其地址空間中脫離。但這不等于將共享內存段從系統內核中移走。當進程脫離成功后,數據結構shmid_ds中元素shm_nattch將減1。當此數值減為0以后,系統內核將物理上把內存段從系統內核中移走。 用共享內存的實例: (1)將字符串寫入到內存段中 (2)從內存段中讀取字符串 (3)改變內存段的權限 (4)刪除內存段 5)使用示例shmtool.c#include<stdio.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/shm.h>#defineSEGSIZE100main(intargc,char*argv[]){key_tkey; intshmid,cntr; char*segptr; if(argc==1) usage();/*Createuniquekeyviacalltoftok()*/ key=ftok(".",'S'); /*Openthesharedmemorysegment-createifnecessary*/ if((shmid=shmget(key,SEGSIZE,IPC_CREAT|IPC_EXCL|0666))==-1) { printf("Sharedmemorysegmentexists-openingasclient\n"); /*Segmentprobablyalreadyexists-tryasaclient*/ if((shmid=shmget(key,SEGSIZE,0))==-1) { perror("shmget"); exit(1); } } else { printf("Creatingnewsharedmemorysegment\n"); } /*Attach(map)thesharedmemorysegmentintothecurrentprocess*/ if((segptr=shmat(shmid,0,0))==-1) { perror("shmat"); exit(1); } switch(tolower(argv[1][0])) { case'w':writeshm(shmid,segptr,argv[2]); break; case'r':readshm(shmid,segptr); break; case'd':removeshm(shmid); break; case'm':changemode(shmid,argv[2]); break; default:usage(); }}writeshm(intshmid,char*segptr,char*text){ strcpy(segptr,text); printf("Done...\n"); }readshm(intshmid,char*segptr){ printf("segptr:%s\n",segptr);}removeshm(intshmid){ shmctl(shmid,IPC_RMID,0); printf("Sharedmemorysegmentmarkedfordeletion\n");}changemode(intshmid,char*mode){ structshmid_dsmyshmds; /*Getcurrentvaluesforinternaldatastructure*/ shmctl(shmid,IPC_STAT,&myshmds);/*Displayoldpermissions*/ printf("Oldpermissionswere:%o\n",myshmds.shm_perm.mode); /*Convertandloadthemode*/ sscanf(mode,"%o",&myshmds.shm_perm.mode);/*Updatethemode*/ shmctl(shmid,IPC_SET,&myshmds); printf("Newpermissionsare:%o\n",myshmds.shm_perm.mode);}usage(){ fprintf(stderr,"shmtool-Autilityfortinkeringwithsharedmemory\n"); fprintf(stderr,"\nUSAGE:shmtool(w)rite<text>\n"); fprintf(stderr,"(r)ead\n"); fprintf(stderr,"(d)elete\n"); fprintf(stderr,"(m)odechange<octalmode>\n"); exit(1);} 3、消息機制相關1)消息緩沖區 數據結構msgbuf是消息數據的模板。雖然此數據結構需要用戶自己定義,但了解系統中有這樣一個數據結構是十分重要的。在linux/msg.h中,此數據結構是這樣定義的: /*messagebufferformsgsndandmsgrcvcalls*/ structmsgbuf{ longmtype;/*typeofmessage*/ charmtext[1];/*messagetext*/ }; 在數據結構msgbuf中共有兩個元素: mtype指消息的類型,它由一個整數來代表,并且,它只能是整數。 mtext是消息數據本身。 因為程序員自己可以重新定義此數據結構。請看下面重新定義的例子: structmy_msgbuf{ longmtype;/*Messagetype*/ longrequest_id;/*Requestidentifier*/ structclientinfo;/*Clientinformationstructure*/ }; 在Linux系統中,這是在linux/msg.h中定義的: #defineMSGMAX4056/*<=4056*//*maxsizeofmessage(bytes)*/消息的最大的長度是4056個字節,其中包括mtype,它占用4個字節的長度。2)系統調用msgget() 如果希望創建一個新的消息隊列,或者希望存取一個已經存在的消息隊列,你可以使用系統調用msgget()。系統調用:msgget();原型:intmsgget(key_tkey,intmsgflg);返回值:如果成功,返回消息隊列標識符 如果失敗,則返回-1:errno=EACCESS(權限不允許) EEXIST(隊列已經存在,無法創建) EIDRM(隊列標志為刪除) ENOENT(隊列不存在) ENOMEM(創建隊列時內存不夠) ENOSPC(超出最大隊列限制) 系統調用msgget()中的第一個參數是關鍵字值(通常是由ftok()返回的)。然后此關鍵字值將會和其他已經存在于系統內核中的關鍵字值比較。這時,打開和存取操作是和參數msgflg中的內容相關的。參數msgflg: IPC_CREAT如果內核中沒有此隊列,則創建它。 IPC_EXCL當和IPC_CREAT一起使用時,如果隊列已經存在,則失敗。如果單獨使用IPC_CREAT,則msgget()要么返回一個新創建的消息隊列的標識符,要么返回具有相同關鍵字值的隊列的標識符。如果IPC_EXCL和IPC_CREAT一起使用,則msgget()要么創建一個新的消息隊列,要么如果隊列已經存在則返回一個失敗值-1。IPC_EXCL單獨使用是沒有用處的。3)系統調用:msgsnd();原型:intmsgsnd(intmsqid,structmsgbuf*msgp,intmsgsz,intmsgflg);返回值:如果成功,0。 如果失敗,-1:errno=EAGAIN(隊列已滿,并且使用了IPC_NOWAIT) EACCES(沒有寫的權限) EFAULT(msgp地址無效) EIDRM(消息隊列已經刪除) EINTR(當等待寫操作時,收到一個信號) EINVAL(無效的消息隊列標識符,非正數 的消息類型,或者無效的消息長度) ENOMEM(沒有足夠的內存復制消息緩沖區 第一個參數是消息隊列標識符,它是由系統調用msgget返回的。第二個參數是msgp,是指向消息緩沖區的指針。參數msgsz中包含的是消息的字節大小,但不包括消息類型的長度(4個字節)。 參數msgflg可以設置為0(此時為忽略此參數),或者使用PC_NOWAIT。如果消息隊列已滿,那么此消息則不會寫入到消息隊列中,控制將返回到調用進程中。如果沒有指明,調用進程將會掛起,直到消息可以寫入到隊列中。 4)系統調用:msgrcv();原型:intmsgrcv(intmsqid,structmsgbuf*msgp,intmsgsz,longmtype,intmsgflg);返回值:如果成功,則返回復制到消息緩沖區的字節數。 如果失敗,則返回-1:errno=E2BIG(消息的長度大于msgsz,沒有MSG_NOERROR) EACCES(沒有讀的權限) EFAULT(msgp指向的地址是無效的) EIDRM(隊列已經被刪除) EINTR(被信號中斷) EINVAL(msgqid無效,或者msgsz小于0) ENOMSG(使用IPC_NOWAIT,同時隊列中的消息無法滿足要求) 第一個參數用來指定將要讀取消息的隊列。第二個參數代表要存儲消息的消息緩沖區的地址。第三個參數是消息緩沖區的長度,不包括mtype的長度,它可以按照如下的方法計算: msgsz=sizeof(structmymsgbuf)-sizeof(long);第四個參數是要從消息隊列中讀取的消息的類型。如果此參數的值為0,那么隊列中最長時間的一條消息將返回,而不論其類型是什么。5)系統調用:msgctl();調用原型:intmsgctl(intmsgqid,intcmd,structmsqid_ds*buf);返回:0,如果成功。 1,如果失敗:errno=EACCES(沒有讀的權限同時cmd是IPC_STAT) EFAULT(buf指向的地址無效) EIDRM(在讀取中隊列被刪除) EINVAL(msgqid無效,或者msgsz小于0) EPERM(IPC_SET或者IPC_RMID命令被使用,但調用程序沒有寫的權限)參數cmd: IPC_STAT 讀取消息隊列的數據結構msqid_ds,并將其存儲在buf指定的地址中。 IPC_SET 設置消息隊列的數據結構msqid_ds中的ipc_perm元素的值。這個值取自buf參數。 IPC_RMID 從系統內核中移走消息隊列。 6)使用示例(1)message.h/*message.h*/#ifndefMESSAGE_H#defineMESSAGE_Hstructmymsgbuf{ longmtype; charmtext[256];};#endif(2)shm_server.c/*shm_server.c*/#include<stdio.h>#include<stdlib.h>#include<string.h>#include<signal.h>#include<sys/msg.h>#include<sys/ipc.h>#include"message.h"intmsqid=-1;voidsig_handle(intsigno){/*軟中斷*/ if(msqid!=-1) msgctl(msqid,IPC_RMID,NULL);printf("serverquit...\n"); exit(0);}intmain(){ structmymsgbufmsgbuf; intleft,right; charc;intlength; if((msqid=msgget(999,0666))!=-1) { msgctl(msqid,IPC_RMID,NULL); } if((msqid=msgget(999,IPC_CREAT|0666))==-1) { printf("error:getmsg\n"); exit(1); } signal(SIGINT,sig_handle);/*LINUX置軟中斷—CTRL-D*/ for(;;){ if(msgrcv(msqid,&msgbuf,256,1L,0)==-1) { printf("error:msgrcv\n"); exit(1); }length=strlen(msgbuf.mtext); left=0; right=length-1; while(left<right) { c=msgbuf.mtext[left]; msgbuf.mtext[left]=msgbuf.mtext[right]; msgbuf.mtext[right]=c;left++;right--; }msgbuf.mtext[length]='\0';msgbuf.mtype=2; if(msgsnd(msqid,&msgbuf,256,0)==-1) { printf("error:msgsnd"); exit(1); }} msgctl(msqid,IPC_RMID,NULL); exit(0);}(3)msg_client.c/*msg_client.c*/#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/msg.h>#include<sys/ipc.h>#include"message.h"intmain(){ structmymsgbufmsgbuf; intmsqid; if((msqid=msgget(999,0666))==-1){printf("Serverisnotrunning\n");exit(1);} printf("Inputaline:"); scanf("%s",msgbuf.mtext);msgbuf.mtype=1; if(msgsnd(msqid,&msgbuf,256,0)==-1) { printf("error:msgsnd\n"); exit(1); } if(msgrcv(msqid,&msgbuf,256,2L,0)==-1) { printf("error:msgrcv\n"); exit(1); } printf("Thereversedlineis:%s\n",msgbuf.mtext); exit(0);}實驗四存儲管理實驗名稱:存儲管理 實驗項目性質:設計性所涉及課程:操作系統 計劃學時:2承擔實驗室:計算機實驗室 實驗環境要求:RedhatLinux適用專業:計算機科學與技術一、實驗目的1、了解虛擬存儲技術的特點,掌握請求頁式存儲管理的主要頁面置換算法原理。2、掌握請求頁式存儲管理中頁面置換算法的模擬設計方法。二、實驗內容設計一個虛擬存儲區和內存工作區,并使用下述方法計算訪問命中率。①先進先出的算法(FIFO); ②最近最少少使用算法(LRR);③最佳淘汰算法(OPT):選淘汰最不常用的頁地址;④最少訪問頁面算法(LFR);⑤最近最不經常使用算法(NUR).(其中③④為選擇內容) 命中率=1-頁面失效次數/頁地址流長度三、實驗指導1、通過隨機數產生一個指令序列,共320條指令。指令的地址按下述原則生成:①50%的指令是順序執行的;②25%的指令是均勻分布在前地址部分;③25%的指令是均勻分布在后地址部分。具體的實施方法是:①在[1,319]指令地址之間隨機選取一起點m;②順序執行一條指令,即執行地址為m十1的指令;③在前地址[0,m十1]中隨機選取一條指令并執行,該指令的地址為m’;④順序執行一條指令,其地址為m‘+1;⑤在后地址[m’+2,319]中隨機選取一條指令并執行;③重復上述步驟①~⑤,直到執行320次指令。2、將指令序列變換成為頁地址流設:①頁面大小為IK;②用戶內存容量為4頁到32頁;③用戶虛存容量為32K。在用戶虛存中,按每K存放10條指令排列虛存地址,即320條指令在虛存中的存放方式為:第0條~第9條指令為第0頁(對應虛存地址為[0,9]);第10條~第19條指令為第1頁(對應虛存地址為[10,19]);第310條~第319條指令為第31頁(對應虛存地址為[310,319])。按以上方式,用戶指令可組成32頁。3、按實驗要求計算并輸出各種算法在不同內存容量下的命中率。 在本實驗中,頁地址流長度為320,頁面失效次數為每次訪問相應指令時,該指令所對應的頁不在內存的次數。4、隨機數產生辦法關于隨機數產生辦法,Linux或UNIX系統提供函數srand()和rand()分別進行初始化和產生隨機數。例如: srand();語句可初始化一個隨機數;a[0]=10*rand()/32767*319+1;a[1]=10*rand()/32767*a[0];….語句可用來產生a[0]與a[1]中的隨機數(rand()產生一個介于0—32767之間的整數)。 5、相關定義(1)數據結構 1)、頁面類型 typedefstruct{ intpn,pfn,counter,time; }pl_type;其中pn為頁號,pfn為面號,counter為一個周期內訪問該頁面次數,time為訪問時間。 2)、頁面控制結構 structpfc_struct{intpn,pfn;structpfc_struct*next;}typedefstructpfc_structpfc_type;pfc_typepfc[total_vp],*freepf_head,*busypf_head;pfc_type*busypf_tail;其中pfc[total_vp]為定義用戶進程虛頁控制結構, *freepf_head為空頁面頭的指針, *busypf_head為忙頁面的頭指針, *busypf_tail為忙頁面的尾指針。 (2)函數定義voidinitialize():初始化函數,給每個相關的頁面賦值。voidFIFO():計算使用FIFO()算法時的命中率。voidLRU():計算使用LRU()算法時的命中率。voidOPT():計算使用OPT()算法時的命中率。voidLFU():計算使用LFU()算法時的命中率。voidNUR():計算使用NUR()算法時的命中率。(3)變量定義inta[total_instruction]:指令流數組。intpage[total_instruction]:每條指令所屬頁號。intoffset[total_instruction]:每頁裝入10條指令后取模運算頁號偏移值。inttotal_pf:用戶進程的內存頁面數。intdiseffect:頁面失效次數。6、程序流程圖〈略〉7、示例程序 #defineTRUE1#defineFALSE0#defineINVALID-1#definenull0#definetotal_instruction320 /*指令流長*/#definetotal_vp32 /*虛頁長*/#defineclear_period50 /*清零周期*/typedefstruct{ /*頁面結構*/ intpn,pfn,counter,time;}pl_type;pl_typepl[total_vp]; /*頁面數組*/structpfc_struct{ /*頁面控制結構*/ intpn,pfn; structpfc_struct*next;};typedefstructpfc_structpfc_type;pfc_typepfc[total_vp],*freepf_head,*busypf_head,*busypf_tail;intdiseffect,a[total_instruction];intpage[total_instruction],offset[total_instruction];voidinitialize(inttotal_pf);voidFIFO(inttotal_pf);voidLRU(inttotal_pf);voidOPT(inttotal_pf);voidLFU(inttotal_pf);voidNUR(inttotal_pf);#include<stdlib.h>#include<stdio.h>#include<time.h>main(){ intS,i; srand((unsigned)time(NULL));/* srand(getpid()*10);*/ /*由于每次運行時進程號不同,故可用來作為初始化隨機數的"種子"*/ S=(float)319*rand()/32767+1; for(i=0;i<total_instruction;i+=4) /*產生指令隊列*/ { a[i]=S; /*任選一指令訪問點*/ a[i+1]=a[i]+1; /*順序執行一條指令*/ a[i+2]=(float)a[i]*rand()/32767; /*執行前地址指令m'*/ a[i+3]=a[i+2]+1; /*執行后地址指令*/ S=(float)rand()*(318-a[i+2])/32767+a[i+2]+2; } for(i=0;i<total_instruction;i++) /*將指令序列變換成頁地址流*/ { page[i]=a[i]/10; offset[i]=a[i]%10; } for(i=4;i<=32;i++) /*用戶內存工作區從4個頁面到32個頁面*/ { printf("%2dpageframes",i); FIFO(i); LRU(i); OPT(i); LFU(i); NUR(i); printf("\n"); } return(0);}//voidFIFO(total_pf) /*FIFO(FirstInFirstOut)ALOGRITHM*///inttotal_pf; /*用戶進程的內存頁面數*/voidFIFO(inttotal_pf)//inttotal_pf;{ inti; pfc_type*p; initialize(total_pf); busypf_head=busypf_tail=NULL; for(i=0;i<total_instruction;i++) { if(pl[page[i]].pfn==INVALID) {diseffect+=1; if(freepf_head==NULL) { p=busypf_head->next; pl[busypf_head->pn].pfn=INVALID; freepf_head=busypf_head; freepf_head->next=NULL; busypf_head=p; } p=freepf_head->next; freepf_head->next=NULL; freepf_head->pn=page[i]; pl[page[i]].pfn=freepf_head->pfn; if(busypf_tail==NULL) busypf_head=busypf_tail=freepf_head; else { busypf_tail->next=freepf_head; busypf_tail=freepf_head; } freepf_head=p; } } printf("FIFO:%6.4f",1-(float)diseffect/320);}voidLRU(inttotal_pf)//inttotal_pf;{ intmin,minj,i,j,present_time; initialize(total_pf); present_time=0; for(i=0;i<total_instruction;i++) { if(pl[page[i]].pfn=INVALID) { diseffect++; if(freepf_head==NULL) { min=32767; for(j=0;j<total_vp;j++) if(min>pl[j].time&&pl[j].pfn!=INVALID) { min=pl[j].time; minj=j; } freepf_head=&pfc[pl[minj].pfn]; pl[min].pfn=INVALID; pl[min].time=-1; freepf_head->next=NULL; } pl[page[i]].pfn=freepf_head->pfn; pl[page[i]].time=present_time; freepf_head=freepf_head->next; } else pl[page[i]].time=present_time; present_time++; } printf("LRU:%6.4f",1-(float)diseffect/320);}voidNUR(inttotal_pf)//inttotal_pf;{ inti,j,dp,cont_flag,old_dp;// pfc_type*t; initialize(total_pf); dp=0; for(i=0;i<total_instruction;i++) { if(pl[page[i]].pfn==INVALID) /*頁面失效*/ { diseffect++; if(freepf_head==NULL) /*無空閑頁面*/ { cont_flag=TRUE; old_dp=dp; while(cont_flag) if(pl[dp].counter==0&&pl[dp].pfn!=INVALID) cont_flag=FALSE; else { dp++; if(dp==total_vp) dp=0; if(dp==old_dp) for(j=0;j<total_vp;j++) pl[j].counter=0; } freepf_head=&pfc[pl[dp].pfn]; pl[dp].pfn=INVALID; freepf_head->next=NULL; } pl[page[i]].pfn=freepf_head->pfn; freepf_head=freepf_head->next; } else pl[page[i]].counter=1; if(i%clear_period==0) for(j=0;j<total_vp;j++) pl[j].counter=0; } printf("NUR:%6.4f",1-(float)diseffect/320);}voidOPT(inttotal_pf) /*OPT(OptionalReplacement)ALOGRITHM*///inttotal_pf;{ inti,j,max,maxpage,d,dist[total_vp]; pfc_type*t; initialize(total_pf); for(i=0;i<total_instruction;i++) { if(pl[page[i]].pfn==INVALID) { diseffect++; if(freepf_head==NULL) { for(j=0;j<total_vp;j++) if(pl[j].pfn!=INVALID) dist[j]=32767; else dist[j]=0; d=1; for(j=i+1;j<total_instruction;j++) { if(pl[page[j]].pfn!=INVALID) dist[page[j]]=d; d++; } max=-1; for(j=0;j<total_vp;j++) if(max<dist[j]) { max=dist[j]; maxpage=j; } freepf_head=&pfc[pl[maxpage].pfn]; freepf_head->next=NULL; pl[maxpage].pfn=INVALID; } pl[page[i]].pfn=freepf_head->pfn; freepf_head=freepf_head->next; } } printf("OPT:%6.4f",1-(float)diseffect/320);}voidLFU(inttotal_pf) /*LFU(leatFrequentlyUsed)ALOGRITHM*///inttotal_pf;{ inti,j,min,minpage; pfc_type*t; initialize(total_pf); for(i=0;i<total_instruction;i++) { if(pl[page[i]].pfn==INVALID) { diseffect++; if(freepf_head==NULL) { min=32767; for(j=0;j<total_vp;j++) { if(min>pl[j].counter&&pl[j].pfn!=INVALID) { min=pl[j].counter; minpage=j; } pl[j].counter=0; } freepf_head=&pfc[pl[minpage].pfn]; pl[minpage].pfn=INVALID; freepf_head=freepf_head->next; } else pl[page[i]].counter++; } } printf("LFU:%6.4f",1-(float)diseffect/320);}voidinitialize(inttotal_pf) /*初始化相關數據結構*///inttotal_pf; /*用戶進程的內存頁面數*/{ inti; diseffect=0; for(i=0;i<total_vp;i++) { pl[i].pn=i; pl[i].pfn=INVALID; /*置頁面控制結構中的頁號,頁面為空*/ pl[i].counter=0; /*頁面控制結構中的訪問次數為0,時間為-1*/ pl[i].time=-1; } for(i=1;i<total_pf;i++) { pfc[i-1].next=&pfc[i]; pfc[i-1].pfn=i-1; } /*建立pfc[i-1]和pfc[i]之間的鏈接*/ pfc[total_pf-1].next=NULL; pfc[total_pf-1].pfn=total_pf-1; freepf_head=&pfc[0]; /*空頁面隊列的頭指針為pfc[0]*/}實驗五設備管理實驗名稱:設備管理 實驗項目性質:設計性所涉及課程:操作系統 計劃學時:4承擔實驗室:計算機實驗室 實驗環境要求:RedhatLinux適用專業:計算機科學與技術一、實驗目的 1、通過實驗,進一步了解設備獨立性的概念;2、通過實驗,掌握Linux下字符設備驅動程序的設計方法3、掌握Linux下可裝入模塊的設計與實現方法。二、實驗內容1、編寫一個可讀寫的字符設備驅動程序,并作為可裝入模塊加載到系統中去。2、設計相應的示例程序,在用戶進程中使用該設備驅動程序進行字符數據的讀寫。三、實驗指導 有關Linux下驅動程序的設計方法及可裝載模塊的設計與實現方法見附錄中的文檔:《LINUX系統下的設備驅動程序設計》一文。示例程序mychardev.c創建一個只讀的文件系統,它由以下函數組成:staticintdevice_open()/*當進程試圖打開設備文件時調用該函數*/staticvoiddevice_release()/*當一個進程要關閉這個設備時,該函數被調用,這個調用不允許失敗.*/staticintdevice_read()/*當一進程要從已打開的設備文件讀數據時該函數被調用.*/staticintdevice_write()/*當進程向這設備寫(目前不支持)時調用本函數*/intinit_module()/*初始化模塊--注冊字符設備*/voidcleanup_module()/*清除內核模塊,從/proc注銷相應文件*/四、實驗步驟: 1、編寫用戶設備驅動程序模塊mychardev.c,編譯該模塊:#gcc-D__KERNEL__-DMODULE-O2-g-Wall-cmychardev.c[Redhat9(內核2.4.20)編譯方法為:#gcc-D__KERNEL__-DMODULE-O2-g-Wall–I/usr/src/linux-2.4.20-8/include-cmychardev.c]2、加載設備驅動程序mychardev.o: #insmodmychardev.o內核將提示主設備號及設備文件名的建立方法。如果有提示內核版不一致的錯誤,加上參數–f。可用lsmod

檢查新模塊是否裝入及系統中安裝的模塊的情況,要卸載該模塊,用

rmmod

模塊名

(不要擴展名.o)。 3、建立設備文件節點(僅需一次) #cd/dev #mknod/dev/mychardevc2540其中mychardev是設備驅動程序中用register_chrdev()注冊的設備名,c表示字符設備,數字254是主設備號,它是在設備驅動程序安裝時提示的數據,也可以從文件/proc/devices中獲取主設備號(這里是254)。對同一個物理設備,可以用同一主設備號建立多個不同的邏輯設備,也就是我們還可以用mknod/dev/mydevc2540命令建立另一個邏輯名為mydev的設備文件,通過該文件去使用相同的物理設備。刪除該結點用命令rm/dev/mychardev。用ls命令查看/dev目錄下的設備文件名的建立情況。也可查看/proc下proc文件系統記錄的設備模塊文件中modules中有無加載的模塊(用法:#cat/proc/modules)。由于現在建立的是一個字符設備,因此可以用less等命令查看文件的內容:less/dev/mydev。 4、編制用戶程序read_dev.c,在用戶進程中使用該設備(從設備中讀取數據),反復運行,觀察結果。 5、修改設備驅動程序中的write()函數,實現對設備的寫操作;同時修改用戶程序,在用戶程序中對設備進行讀寫。 注意:設備驅動程序修改后,在重新裝入前,應先把裝入的驅動程序卸載。 6、卸載設備驅動程序 #rmmodmychardev五、示例程序:一個只讀的字符設備。 1、mychardev.c/*mychardev.c創建一個只讀的文件系統*/#ifndefMODULE#defineMODULE#endif//Redhat9下#include<linux/devfs_fs_kernel.h>#include<linux/config.h>#include<linux/module.h>#include<linux/kernel.h>/*printk()*/#include<linux/init.h>#include<asm/timex.h>/*字符設備所需*/#include<linux/fs.h>#include<linux/wrapper.h>/*為兼容后續版本*///#include<linux/malloc.h>#include<linux/slab.h>#include<asm/system.h>#include<linux/proc_fs.h>#include<linux/fcntl.h>//#include<asm/uaccess.h>#include<asm/segment.h>#defineSUCCESS0/*設備定義**********************//*設備名,它將出現在/proc/devices*/#defineDEVICE_NAME"mychardev"/*該設備最大信息長度*/#defineBUF_LEN80/*設備否打開?利用它防止當前進程使用同一設備*/staticintDevice_Open=0;/*提示信息*/charMessage[BUF_LEN];/*進程讀取的信息的指針*/char*Message_Ptr;/*當進程試圖打開設備文件時調用該函數*/staticintdevice_open(structinode*inode,structfile*file){staticintcounter=0;#ifdefDEBUGprintk("device_open(%p,%p)\n",inode,file);#endif/*兩個進程不得同時對同一設備操作.*/if(Device_Open)return-EBUSY;Device_Open++;/*初始化信息.*/sprintf(Message,"IfItoldyouonce,Itoldyou%dtimes-Hello,world!\n",counter++);/*僅在輸出信息的最大長度大于BUF_LEN,(這兒是80)使用sprintf.*注意不得超過緩沖區長度,尤其是在內核中!!*/Message_Ptr=Message;/*當文件打開時確信這個模塊存在.*/MOD_INC_USE_COUNT;returnSUCCESS;}/*當一個進程要關閉這個設備時,該函數被調用,這個調用不允許失敗.*/staticintdevice_release(structinode*inode,structfile*file){#ifdefDEBUGprintk("device_release(%p,%p)\n",inode,file);#endifDevice_Open--;/*設備文件使用計數器減1*/MOD_DEC_USE_COUNT; return0;}/*當一進程要從已打開的設備文件讀數據時該函數被調用.*/staticssize_tdevice_read(structfile*file,char*buffer,/*獲得填充數據的緩沖區*/size_tlength,loff_t*fops)/*緩沖區中的數據長度(絕對不能越界!)*/{/*已寫入到緩沖區buffer中的確切字節數*/intbytes_read=0;char*ptr;intlen=0;#ifdefDEBUGprintk("device_read(%p,%p,%p,%d)\n",inode,file,buffer,length);#endif/*如果已位于信息尾部,返回0*/if(*Message_Ptr==0)return0;ptr=Message_Ptr;len=0;/*取字符串的長度*/while(*(ptr++)) len++;if(length<len)len=length;copy_to_user(buffer,Message_Ptr,len);/*準確地把數據送到緩沖區buffer*/bytes_read=len;#ifdefDEBUGprintk("Read%dbytes,%dleft\n",bytes_read,length);#endif/*返回讀取的實際字節數據*/returnbytes_read;}/*當進程向這設備寫(目前不支持)時調用本函數*/staticssize_tdevice_write(structfile*file, constchar*buffer,size_tlength,loff_t*fops){#ifdefDEBUGprintk("device_write(%p,%p,%s,%d)",inode,file,buffer,length);#endif intlen; if(length>80) len=80; else len=length; copy_from_user(Message_Ptr,buffer,len);return-EINVAL;}/*模塊定義***********************//*設備的主設備號*/staticintMajor;/*當進程要對創建的設備進行某些操作時,這個結構存放了要調用的函數的入口*這個結構有系統設備表的指針指向。NULL表示未實現該功能。*/structfile_operationsFops={ read: device_read, write: device_write, open: device_open, release:device_release/*a.k.a.close*/};/*初始化模塊--注冊字符設備*/intinit_module(){/*注冊字符設備(至少一次)*/Major=register_chrdev(0,DEVICE_NAME,&Fops);/*負值意味出錯*/if(Major<0){printk("Sorry,registeringthecharacterdevicefailedwith%d\n",Major);returnMajor;}printk("Registerationisasuccess.Themajordevicenumberis%d.\n",Major);printk("Ifyouwanttotalktothedevicedriver,you'llhaveto\n");printk("createadevicefile.Wesuggestyouuse:\n");printk("mknod<name>c%d0\n",Major);return0;}/*清除模塊--從/proc中注銷合適的文件*/voidcleanup_module(){intret;/*注銷設備*/ret=unregister_chrdev(Major,DEVICE_NAME);/*如果出錯,報告錯誤*/if(ret<0)printk("Errorinmodule_unregister_chrdev:%d\n",ret);}/*在Redhat7.3以下版本請將其注釋掉*/MODULE_LICENSE("GPL");2、read_dev.c/*read_dev.c使用用戶設備驅動程序的示例*/#include<stdio.h>#include<linux/fs.h>main(){ inti,j,num; intf; charbuffer[80]; f=open("/dev/mychardev",0); printf("Howmanycharsdoyouread:(1-80)"); scanf("%d",&num); i=read(f,buffer,num); j=0; while(j<i){ printf("%c",buffer[j]); j++;} close(f); printf("\n");}六、UNIX/LUNIX下設備驅動程序的基本結構在UNIX系統里,設備驅動程序隱藏了設備的具體細節,對各種不同設備提供了一致的接口,把設備映射為一個特殊的設備文件,用戶程序可以象對其它文件一樣對此設備文件進行操作。UNIX對硬件設備支持兩個標準接口:塊特別設備文件和字符特別設備文件,通過塊(字符)特別設備文件存取的設備稱為塊(字符)設備或具有塊(字符)設備接口。設備由一個主設備號和一個次設備號標識。主設備號唯一標識了設備類型,即設備驅動程序類型,它是塊設備表或字符設備表中設備表項的索引。次設備號僅由設備驅動程序解釋,一般用于識別在若干可能的硬件設備中,I/O請求所涉及到的那個設備。設備驅動程序可以分為三個主要組成部分:(1)自動配置和初始化子程序,負責檢測所要驅動的硬件設備是否存在和是否能正常工作。如果該設備正常,則對這個設備及其相關的、設備驅動程序需要的軟件狀態進行初始化。這部分驅動程序僅在初始化的時候被調用一次。(2)服務于I/O請求的子程序,又稱為驅動程序的上半部分。調用這部分是由于系統調用的結果。這部分程序在執行的時候,系統仍認為是和進行調用的進程屬于同一個進程,只是由用戶態變成了核心態,具有進行此系統調用的用戶程序的運行環境,因此可以在其中調用sleep()等與進程運行環境有關的函數。(3)中斷服務子程序,又稱為驅動程序的下半部分。在UNIX系統中,并不是直接從中斷向量表中調用設備驅動程序的中斷服務子程序,而是由UNIX系統來接收硬件中斷,再由系統調用中斷服務子程序。中斷可以產生在任何一個進程運行的時候,因此在中斷服務程序被調用的時候,不能依賴于任何進程的狀態,也就不能調用任何與進程運行環境有關的函數。因為設備驅動程序一般支持同一類型的若干設備,所以一般在系統調用中斷服務子程序的時候,都帶有一個或多個參數,以唯一標識請求服務的設備。在系統內部,I/O設備的存取通過一組固定的入口點來進行,這組入口點是由每個設備的設備驅動程序提供的。一般來說,字符型設備驅動程序能夠提供如下幾個入口點:(1)open入口點。打開設備準備I/O操作。對字符特別設備文件進行打開操作,都會調用設備的open入口點。ope

溫馨提示

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

評論

0/150

提交評論