杭電計算機(jī)操作系統(tǒng)課程設(shè)計指導(dǎo)書_第1頁
杭電計算機(jī)操作系統(tǒng)課程設(shè)計指導(dǎo)書_第2頁
杭電計算機(jī)操作系統(tǒng)課程設(shè)計指導(dǎo)書_第3頁
杭電計算機(jī)操作系統(tǒng)課程設(shè)計指導(dǎo)書_第4頁
杭電計算機(jī)操作系統(tǒng)課程設(shè)計指導(dǎo)書_第5頁
已閱讀5頁,還剩123頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

操作系統(tǒng)課程設(shè)計指導(dǎo)書

趙偉華梁紅兵劉真

2015年9月

計算機(jī)學(xué)院

目錄

第一章操作系統(tǒng)課程設(shè)計的內(nèi)容與實施方法-3-

1.1操作系統(tǒng)課程設(shè)計總體要求-3-

1.2操作系統(tǒng)課程設(shè)計的內(nèi)容-3-

1.3操作系統(tǒng)課程設(shè)計實施方案-4-

第二章基于DOS的多任務(wù)系統(tǒng)的實現(xiàn)-5-

2.1設(shè)計目的和內(nèi)容要求-5-

2.2線程描述-6-

2.3線程的創(chuàng)建和撤消-8-

2.4線程調(diào)度設(shè)計-10-

2.5基本實例程序的實現(xiàn)-23-

2.6線程的阻塞和喚醒-26-

2.7線程的同步與互斥-26-

2.8利用消息緩沖隊列通信機(jī)制實現(xiàn)線程間通信-27-

第三章簡單文件系統(tǒng)的實現(xiàn)-32-

3.1設(shè)計目的和內(nèi)容要求-32-

3.2預(yù)備知識-33-

3.3實例系統(tǒng)的設(shè)計與實現(xiàn)-36-

第四章:linux進(jìn)程管理-47-

4.1設(shè)計目的和內(nèi)容要求-47-

一、設(shè)計目的-47-

二、設(shè)計內(nèi)容-47-

三、思考-48-

4.2Linux基本使用:-48-

一、進(jìn)入Linux系統(tǒng)和退出Linux系統(tǒng)-48-

二、圖形化界面-49-

三、常用命令-50-

四、Linux的在線幫助man-73-

五、vi和vim編輯器-75-

六、Linux環(huán)境下C編程-79-

七、gdb調(diào)試工具-81-

4.3linux內(nèi)核模塊的編程入門-83-

一、模塊編程步驟:-84-

二、模塊編程舉例:-84-

4.4linux進(jìn)程管理系統(tǒng)調(diào)用-87-

一、linux進(jìn)程控制-87-

二、進(jìn)程通信-95-

(一)Linux管道通信機(jī)制-95-

(-)Linux消息隊列通信機(jī)制-101-

(三)linux共享內(nèi)存通信-108-

三、進(jìn)程/線程同步(信號量)-118-

(―)SystemV信號量-118-

(二)Posix信號量:-119-

操作系統(tǒng)課程設(shè)計

第一章操作系統(tǒng)課程設(shè)計的內(nèi)容與實施方法

1.1操作系統(tǒng)課程設(shè)計總體要求

1.遵守機(jī)房紀(jì)律,服從機(jī)房調(diào)度。

2.課程設(shè)計的設(shè)計和上機(jī)調(diào)試要求獨立完成,不能拷貝。

3.上機(jī)前,努力準(zhǔn)備上機(jī)內(nèi)容,并預(yù)先作一些情況分析。

4.仔細(xì)觀察上機(jī)時出現(xiàn)的各種現(xiàn)象,記錄上機(jī)的結(jié)果。

5.認(rèn)真書寫課程設(shè)計報告。報告中應(yīng)包括:課程設(shè)計的目的及要求、程序的設(shè)計思想

及流程圖、程序調(diào)試中遇到的問題及分析、程序代碼清單和結(jié)果分析;程序的不足之處及修

改方案等。程序要帶注釋。

1.2操作系統(tǒng)課程設(shè)計的內(nèi)容

本次課程設(shè)計共設(shè)置了以下兩個題目:

1.基于DOS的多任務(wù)系統(tǒng)的實現(xiàn)

DOS系統(tǒng)是一個典型的單用戶單任務(wù)操作系統(tǒng)?!盎贒OS的多任務(wù)系統(tǒng)的實現(xiàn)”的

基本設(shè)計思想是設(shè)計一個運(yùn)行在DOS系統(tǒng)中的應(yīng)用程序,該應(yīng)用程序能實現(xiàn)多線程機(jī)制,

即能完成所有與線程管理有關(guān)的工作,包括線程創(chuàng)建與撤銷、線程阻塞與喚醒、線程互斥與

同步、線程調(diào)度、線程通信等。我們利用這些功能創(chuàng)建多個線程,并調(diào)度這些線程在CPU

上并發(fā)執(zhí)行,每個線程執(zhí)行一個函數(shù)完成指定的功能。

2.簡單文件系統(tǒng)的實現(xiàn)

文件系統(tǒng)是操作系統(tǒng)內(nèi)核中非常重要的組成部分之一。一個相對完整的文件系統(tǒng)應(yīng)該

具備以下幾個方面的功能:磁盤存儲空間管理、目錄管理、文件讀寫管理、文件保護(hù)與共享。

由于對磁盤的存取操作必然涉及到磁盤驅(qū)動程序設(shè)計,為了降低設(shè)計難度,本實驗的基本設(shè)

計思想是在內(nèi)存中申請一塊存儲空間作為虛擬磁盤,在其上建立一個類似于FAT的文件系

統(tǒng),所有對文件系統(tǒng)的操作都是在該虛擬磁盤空間中進(jìn)行。為了保存該文件系統(tǒng)中的內(nèi)容,

如我們創(chuàng)建的目錄、文件等,在退出文件系統(tǒng)的使用之前必須將整個虛擬磁盤上的內(nèi)容以一

個文件的形式全部保存到系統(tǒng)真正的磁盤上;以后想再次使用該文件系統(tǒng)時又必須首先從磁

盤上讀入這個文件的內(nèi)容到內(nèi)存中的虛擬磁盤上,然后才能繼續(xù)使用。

1.3操作系統(tǒng)課程設(shè)計實施方案

操作系統(tǒng)是計算機(jī)系統(tǒng)中最核心最重要的一組軟件集合,用來控制系統(tǒng)中的所有硬件及

其他軟件的運(yùn)行,各程序模塊內(nèi)部的控制流程及相互間的接口都很復(fù)雜。本課程設(shè)計雖然只

是實現(xiàn)其中的一部分功能,但對學(xué)生的綜合要求依然較高,既要求對原理知識的綜合掌握,

又要求具有一定的C語言編程能力,特別是“基于DOS的多任務(wù)系統(tǒng)的實現(xiàn)”這個題目,

由于要利用TurboC的interrupt類型的函數(shù)來實現(xiàn)線程切換過程中的線程運(yùn)行現(xiàn)場及環(huán)境信

息的自動保存及恢復(fù),因此程序開發(fā)工具是采用字符型界面的TurboC。而不同學(xué)生在編程

能力上存在差異,旦大多數(shù)學(xué)生對字符型界面的開發(fā)平臺存在畏懼心理,為了達(dá)到因材施教

的目的,保證每個學(xué)生都能根據(jù)自己的實際情況參與到課程設(shè)計過程中,我們開發(fā)設(shè)計了一

個可視化的操作系統(tǒng)課程設(shè)計平臺軟件(該平臺軟件的使用方法見后面第三篇內(nèi)容),該軟

件系統(tǒng)最大的特點是提供了模塊式替換功能,即將每個課程設(shè)計題目的內(nèi)容分解成若干個相

對“較小”的功能模塊(模塊具體劃分情況見后面課程設(shè)計的詳細(xì)介紹),允許每個學(xué)生根

據(jù)自身能力情況選擇實現(xiàn)課程設(shè)計的全部或部分功能模塊,學(xué)生完成一個或多個模塊后可在

軟件系統(tǒng)中進(jìn)行模塊替換操作,替換后需要重新進(jìn)行編譯、鏈接工作,然后就可以運(yùn)行程序,

從而及時看到所編寫模塊的功能實現(xiàn)情況。這樣能夠提高所有學(xué)生主動學(xué)習(xí)的興趣,提高實

際動手能力。

第二章基于DOS的多任務(wù)系統(tǒng)的實現(xiàn)

2.1設(shè)計目的和內(nèi)容要求

1.設(shè)計目的

通過對線程(和進(jìn)程)的創(chuàng)建和撤消、CPU的調(diào)度、同步機(jī)制、通信機(jī)制的實現(xiàn),達(dá)

到以下目的:

(1)加深對線程和進(jìn)程概念的理解,明確進(jìn)程和程序的區(qū)別。

(2)加深對CPU調(diào)度過程(現(xiàn)場保護(hù)、CPU的分派和現(xiàn)場恢復(fù))的理解。

(3)進(jìn)一步認(rèn)識并發(fā)執(zhí)行的概念,明確順序執(zhí)行和并發(fā)執(zhí)行的區(qū)別。

(4)加深對臨界資源、臨界區(qū)、信號量以及同步機(jī)制的理解。

(5)加深對消息緩沖通信的理解。

2.內(nèi)容要求

(1)用C語言完成線程的創(chuàng)建和撤消,并按先來先服務(wù)方式對多個線程進(jìn)行調(diào)度。

(2)將線程調(diào)度算法修改為時間片輪轉(zhuǎn)算法,實現(xiàn)時間片輪轉(zhuǎn)調(diào)度。(也可以結(jié)合

優(yōu)先權(quán),實現(xiàn)優(yōu)先權(quán)加時間片輪轉(zhuǎn)算法的線程調(diào)度。)

(3)改變時間片的大小,觀察結(jié)果的變化。思考:為什么時間片不能太小或太大。

(4)假設(shè)兩個線程共用同一軟件資源(如某一變量,或某一數(shù)據(jù)結(jié)構(gòu)),請用記錄

型信號量來實現(xiàn)對它的互斥訪問。

(5)假設(shè)有兩個線程共享一個可存放5個整數(shù)的緩沖,其中一個線程不停地計算

1至50的平方,并將結(jié)果放入緩沖中,另一個線程不斷地從緩沖中取出結(jié)果,并將它們

打印出來,請用記錄型信號量實現(xiàn)這一生產(chǎn)者和消費者的同步問題。

(6)實現(xiàn)消息緩沖通信,并與4、5中的簡單通信進(jìn)行比較。

(7)思考:在線程間進(jìn)行消息緩沖通信時,若對消息隊列的訪問沒有滿足互斥要

求,情況將會怎樣?

3.學(xué)時安排(共21學(xué)時)

(1)授課3學(xué)時,內(nèi)容包括線程的創(chuàng)建、撤消、調(diào)度等內(nèi)容。

(2)線程的創(chuàng)建、撤消、先來先服務(wù)調(diào)度,8學(xué)時上機(jī)。

(3)時間片輪轉(zhuǎn)調(diào)度,3學(xué)時上機(jī)。

(4)信號量的實現(xiàn),3學(xué)時上機(jī)。

(5)線程間的消息緩沖隊列通信,4學(xué)時上機(jī)。

4.開發(fā)平臺

TurboC2.0或3.0。

2.2線程描述

2.2.1線程基本概念

在一些多任務(wù)的環(huán)境下,用戶可以同時運(yùn)行多個完整的程序。例如,在UNIX環(huán)境

下,你可以用CC命令編譯一個C程序,并把它作為一個后臺進(jìn)程運(yùn)行(只需在命令行后加

上字符'&');在前臺,你又可以做其他的事情,比如,編輯另一個文件。我們把這種系統(tǒng)

稱為基于進(jìn)程的多任務(wù)系統(tǒng)。另外有一種多任務(wù)系統(tǒng),在其下,一個程序的多個部分可同時

運(yùn)行,我們把這種環(huán)境下的任務(wù),即程序的每個部分叫做線程,稱這種系統(tǒng)為基于線程的多

任務(wù)系統(tǒng)。在這種環(huán)境下,處理機(jī)的調(diào)度單位為線程,它們共享整個進(jìn)程的資源,還擁有一

些自己的私有資源。我們將通過本課程設(shè)計實現(xiàn)多個線程的并發(fā)執(zhí)行。

線程,有時也叫做輕進(jìn)程(lightweightprocess),是CPU調(diào)度的基本單位,每個線程有

自己的一個指令計數(shù)器、一組寄存器和一個私有堆棧。而代碼段、數(shù)據(jù)段以及操作系統(tǒng)的其

它資源(如打開的文件)是由一組線程共享的,這一組線程組成一個Task(傳統(tǒng)的進(jìn)程,

即heavyweightprocess相當(dāng)于只有一個線程的Task)。

在許多方面,對線程的操作類似于進(jìn)程:線程可處于就緒、阻塞、執(zhí)行三種狀態(tài)之一;

線程可共享CPU,在單機(jī)系統(tǒng)中,任何時刻最多只能有一個線程處于執(zhí)行狀態(tài);一個Task

中的多個線程可并發(fā)執(zhí)行。但與進(jìn)程不同,一個Task中的多個線程并不互相獨立,因為,

所有線程均可訪問所屬Task的地址空間的任一單元,所以,一個線程讀寫其它線程的私有

堆棧是十分容易的,即系統(tǒng)不提供線程間的保護(hù)。

注:上面講的Task是指一個完整的作業(yè),其中可包括多個線程,與本課程設(shè)計中所講

的多任務(wù)中的任務(wù)(系統(tǒng)中可并發(fā)執(zhí)行的部分,如線程或進(jìn)程)含義不同,除此之外,本課

程設(shè)計中所提到的Task或任務(wù)均代表后者。

線程的切換只需切換寄存器組的值,而不需做有關(guān)內(nèi)存管理方面的工作,實現(xiàn)起來也就

比較簡單。

2.2.2線程控制塊

與進(jìn)程類似,基于線程的多任務(wù)系統(tǒng)中的任務(wù),即線程,它不單是指靜態(tài)的、可并發(fā)執(zhí)

行的程序段本身,其實也是一個動態(tài)的概念,是指可并發(fā)執(zhí)行的程序段及其執(zhí)行過程。因此,

我們要用一個類似于進(jìn)程控制塊PCB的數(shù)據(jù)結(jié)構(gòu)一一線程控制塊TCB,來記錄有關(guān)描述線

程情況和控制線程運(yùn)行所需的全部信息,具體來說,在一個TCB中主要應(yīng)包括以下幾方面

的信息:

1.有關(guān)線程私有堆棧的信息

在線程調(diào)度的過程中,為了保護(hù)線程的現(xiàn)場信息,每個線程都必須有自己的私有堆棧。

我們把被切換線程的現(xiàn)場信息,包括目前各寄存器的值和下一條指令的地址都保存在它的堆

棧中,再從新線程的私有堆棧中恢復(fù)出一組新值來布置系統(tǒng)的寄存器,并從私有堆棧中得到

新線程的下一條指令地址。另外,每個線程中用到的局部變量也是存放在它自己的私有堆棧

中的。因此,在TCB中必須有線程的私有堆棧的信息,包括它在內(nèi)存的起始地址、堆棧

的棧頂指針的段地址和偏移等信息。

DOS中內(nèi)存的地址是20位的,而且DOS的內(nèi)存管理采用分段的方式,每個段的基址

的低4位必須為0,指令和數(shù)據(jù)的邏輯地址可用兩個16位的整數(shù)來描述,即:段地址seg

和段內(nèi)偏移off,其中段地址seg中有段基址的高16位,故邏輯地址seg:off對應(yīng)的物理地址

為segXZ,+off。C語言經(jīng)常用指針來描述一個地址,TurboC提供了三個宏函數(shù)用來實現(xiàn)指

針方式到段地址、偏移地址方式的相互轉(zhuǎn)換:若P為一個指針,則可通過FP_SEG(p)得到該

地址的段地址,F(xiàn)P_OFF(p)得到該地址的段內(nèi)偏移;若seg為一個地址的段地址,off為其段

內(nèi)偏移,則可通過MK_FP(seg,off)得到對應(yīng)的指針。

2.有關(guān)線程的狀態(tài)的信息

在基于線程的多任務(wù)系統(tǒng)中,一個線程的狀態(tài)在它的生命周期中是在不斷地變化的,在

此,我們把線程的主要狀態(tài)劃分為:就緒、執(zhí)行、阻塞和終止態(tài)。如果,一個線程擁有CPU,

我們就說它處于執(zhí)行態(tài);如果它現(xiàn)在雖不在執(zhí)行,但一旦獲得CPU就可執(zhí)行,我們就說它

處于就緒態(tài);如果它在等待CPU以外的其他資源,則說它處于阻塞狀態(tài);如果線程所對應(yīng)

的程序段已運(yùn)行完畢,則它處于終止?fàn)顟B(tài)。因此,在TCB中要設(shè)置一狀態(tài)字段,用來記錄

各線程的現(xiàn)行狀態(tài)。

3.線程的標(biāo)識符

線程標(biāo)識符用于惟一地標(biāo)識一個線程,與進(jìn)程一樣,通常一個線程有兩個標(biāo)識符:

(1)外部標(biāo)識符:它由創(chuàng)建者提供,通常是一個由字母、數(shù)字組成的字符串,記錄在

線程的TCB中。

(2)內(nèi)部標(biāo)識符,它通常是一個整數(shù),由多任務(wù)系統(tǒng)在創(chuàng)建線程時設(shè)置。在本課程設(shè)

計中,我們在多任務(wù)系統(tǒng)的初始化過程中,設(shè)置了一個struct類型的TCB數(shù)組來統(tǒng)一為各

新建線程提供空白TCB,為了簡單起見,我們可以隱含地用各線程所分配到的TCB在整個

TCB數(shù)組中的下標(biāo)來表示該線程的內(nèi)部標(biāo)識符,所以不需要再專門記錄在TCB中了。

4.其它信息

TCB中記錄的信息量可隨系統(tǒng)的復(fù)雜情況而變化,如當(dāng)采用優(yōu)先權(quán)算法進(jìn)行調(diào)度時,在

TCB中還必須設(shè)置優(yōu)先權(quán)字段;當(dāng)TCB要按某種方式排隊時,在其中必須設(shè)置一鏈接指針

字段;當(dāng)必須喚醒因某種原因而阻塞的相關(guān)線程時,則必須設(shè)置阻塞原因字段;在使用消息

緩沖隊列機(jī)制實現(xiàn)線程通信時,則必須設(shè)置通信機(jī)制需要的字段,如接收線程的消息隊列隊

首指針、消息隊列的互斥信號量和資源信號量等。

用C語言來描述,一個最簡單的TCB的數(shù)據(jù)結(jié)構(gòu)可以表示如下:

/*狀態(tài)碼常量定義*/

/*null0notassigned*/

#defineFINISHED0/*表示線程處于終止態(tài)或TCB是空白狀態(tài)*/

#defineRUNNING1/*表示線程處于運(yùn)行態(tài)*/

#defineREADY2/*表示線程處于就緒態(tài)*/

#defineBLOCKED3/*表示線程處于阻塞態(tài)*/

structTCB{

unsignedchar*stack;/*線程堆棧的起始地址*/

unsignedss;/*堆棧段址*/

unsignedsp;/*堆棧指針*/

charstate;/*線程狀態(tài),取值可以是FINISHED、RUNNING.READY、BLOCKED*/

charname[10];/*線程的外部標(biāo)識符*/

}tcbfNTCB];/*NTCB是系統(tǒng)允許的最多任務(wù)數(shù)*/

2.3線程的創(chuàng)建和撤消

2.3.1線程的創(chuàng)建

在創(chuàng)建一個新線程時,線程的創(chuàng)建者必需提供一些信息,如線程的外部標(biāo)識符、線程所

需的私有堆??臻g的大小、與線程所對應(yīng)的程序段的入口地址的有關(guān)信息(這里假設(shè)一個線

程執(zhí)行程序里的一個函數(shù),所以創(chuàng)建者只需提供線程要執(zhí)行的函數(shù)的函數(shù)名即可)。

1.線程創(chuàng)建函數(shù)格式說明

(1)函數(shù)申明原型:typedefint(far*codeptr)(void);/*定義了一個函數(shù)指針類型*/

Intcreate(char*name,codeptrcode,intstck);

(2)函數(shù)功能描述:在main。函數(shù)中調(diào)用,創(chuàng)建一個新線程,讓其執(zhí)行code開始的代

碼。

(3)輸入:

name:新創(chuàng)建線程的外部標(biāo)識符;

code:新創(chuàng)建線程要執(zhí)行的代碼的入口地址,此處用函數(shù)名作為傳入地址;

stck:新創(chuàng)建線程的私有堆棧的長度。

(4)輸出:新創(chuàng)建線程的內(nèi)部標(biāo)識符,若創(chuàng)建失敗,返回-1

2.函數(shù)實現(xiàn)的算法描述

在創(chuàng)建一個線程時主要應(yīng)完成以下工作:

(1)為新線程分配一個空閑的線程控制塊TCB,該TCB的數(shù)組下標(biāo)即為新線程

的內(nèi)部標(biāo)識符。如果沒有空閑的TCB,則返回-1,創(chuàng)建失敗。

(2)為新線程的私有堆棧分配內(nèi)存空間(因為同一進(jìn)程的多個線程共享該進(jìn)程的

程序段和數(shù)據(jù)段空間,所以創(chuàng)建線程時不必象創(chuàng)建進(jìn)程那樣再為程序段和數(shù)據(jù)段分配內(nèi)存空

間)。

(3)初始化新線程的私有堆棧,即按CPU調(diào)度時現(xiàn)場信息的保存格式布置堆棧,

這一點是非常重要的,因為當(dāng)CPU首次調(diào)度該線程運(yùn)行時,CPU中的SS寄存器和SP寄存

器將指向該線程的私有堆棧,并從該堆棧中獲得線程運(yùn)行的正確的指令地址和其它現(xiàn)場信

息。新線程的首次執(zhí)行是從對應(yīng)函數(shù)的入口開始的;而且,執(zhí)行時CPU的寄存器ES、DS

應(yīng)置上恰當(dāng)?shù)闹担籉lags寄存器的允許中斷位也應(yīng)置上1,這樣,線程執(zhí)行過程中才允許硬

中斷(如時鐘中斷)發(fā)生并及時響應(yīng)中斷;其它寄存器(AX、BX、CX、DX、SLDLBP)

的值只在線程執(zhí)行過程中才有意義,它們的初值可為任意值。初始化工作完成后堆棧中各信

息項的值及其相應(yīng)位置如圖2-lb所示。

為了方便堆棧的初始化工作,我們可以按照堆棧中的內(nèi)容設(shè)計一個以下的數(shù)據(jù)結(jié)構(gòu):

structint_regs{

unsignedbp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flags,off,seg;

);

然后用一個指向該數(shù)據(jù)結(jié)構(gòu)的指針給堆棧賦值。

(4)初始化線程控制塊,即填入線程的外部標(biāo)識符,設(shè)置好線程私有堆棧的始址、

段址和棧頂指針,將線程的狀態(tài)置成就緒態(tài)READY,如圖2-la所示。

另外,如果線程調(diào)度算法是按優(yōu)先權(quán)方式進(jìn)行CPU調(diào)度,則需在TCB中置上新線程的

優(yōu)先權(quán)信息(初始優(yōu)先數(shù)可由用戶提供);若TCB的組織方式是按某種方式拉鏈,系統(tǒng)設(shè)置

了線程就緒隊列,則還需將新線程的TCB插入就緒隊列;如果要實現(xiàn)通信,還需要將線程

的消息隊列隊首指針設(shè)置為Nun、消息隊列的互斥信號量和資源信號量分別設(shè)置為{1,Null}

和{0,Null}

(5)最后,返回新線程的內(nèi)部標(biāo)識符。

在TurboC的small編譯模式下,調(diào)用create("f1",(codeptr)f1,1024)創(chuàng)建一個對應(yīng)于函

數(shù)fl()的線程后新線程的內(nèi)存映象如圖2-1所示。

線程私有

堆??臻g

—59ba:63e

TCB集

S59ba:a22

?新sp

BP

TCB(O)線函數(shù)fl()

程DI\57f7:879

初SI

始DS:59ba

現(xiàn)ES:59ba

場DX

stack:63e信CX

TCB(i)ss:59ba息BX

sp:a22AX

state:READY函數(shù)

IP:879over()

name:“fl”

CS:57f757f7:466

Flags:200

Fl()返址

off:466

seg:57f7

TCB(NTCB-l)59ba:a3e

圖a圖b

圖2-1對應(yīng)函數(shù)fl()的新線程的內(nèi)存映像

2.3.2線程的撤消

引起線程撤銷的原因主要有兩個:一是系統(tǒng)或用戶要求撤銷某個線程;二是當(dāng)前線程所

對應(yīng)的函數(shù)已經(jīng)運(yùn)行完成。對于第一種情況比較簡單,只需調(diào)用線程撤銷原語將指定線程撤

銷即可;對于第二鐘情況,首先必須自動調(diào)用線程撤銷原語撤銷當(dāng)前已經(jīng)運(yùn)行完成的線程,

然后還需要自動地重新進(jìn)行CPU調(diào)度。

1.線程撤銷函數(shù)設(shè)計:

(1)函數(shù)申明原型:voiddestroy(intid);

(2)功能:撤銷內(nèi)部標(biāo)識符為id的指定線程。

(3)輸入:

id:將要被撤銷的線程的內(nèi)部標(biāo)識符。

(4)輸出:無

(5)函數(shù)實現(xiàn)的算法描述:

撤銷線程所要完成的工作比較簡單,主要是將線程所占據(jù)的資源歸還給系統(tǒng)。在操作系

統(tǒng)原理中已經(jīng)介紹了線程本身基本不占據(jù)資源,它與同進(jìn)程的其他線程共享該進(jìn)程的代碼段

和數(shù)據(jù)段空間;但是線程作為一個可以獨立調(diào)度和運(yùn)行的基本單元也擁有一些必不可少的資

源,如線程控制塊TCB和私有堆棧。所以撤銷線程所要做的事情主要就是兩個:

(1)將線程的私有堆棧所占的內(nèi)存空間歸還給系統(tǒng);

(2)將線程控制塊TCB的各成員變量進(jìn)行初始化操作。

2.撤銷線程并重新進(jìn)行調(diào)度

前面提到如果是因為當(dāng)前線程運(yùn)行完成而引起線程撤消,則系統(tǒng)應(yīng)能自動撤消該線程,

并重新進(jìn)行CPU調(diào)度。我們可以設(shè)置一個稱為over()的函數(shù)來完成這個工作,該函數(shù)需

要順序做兩件事情:首先調(diào)用destroy()撤銷當(dāng)前線程,然后重新進(jìn)行CPU調(diào)度。所以現(xiàn)

在關(guān)鍵的問題是在當(dāng)前線程運(yùn)行完成后CPU應(yīng)能自動轉(zhuǎn)去執(zhí)行overO,這可通過在創(chuàng)建線

程時進(jìn)行一些相關(guān)的處理來實現(xiàn):在進(jìn)行堆棧初始化時可預(yù)先將。ver()的入口地址壓入線

程的私有堆棧中,如前面圖2-3b所示;這樣,當(dāng)線程所對應(yīng)的函數(shù)正常結(jié)束時,over。函數(shù)

的入口地址將作為函數(shù)的返回地址被彈出至CPU的CS、1P寄存器,從而使CPU的控制權(quán)

自動轉(zhuǎn)向over。去執(zhí)行。

2.4線程調(diào)度設(shè)計

2.4.1CPU調(diào)度中的關(guān)鍵問題

CPU調(diào)度所要做的事情是保護(hù)舊線程的現(xiàn)場、找到新線程、恢復(fù)新線程的現(xiàn)場、并把

處理機(jī)交給新線程讓它執(zhí)行。其中,找一新線程是比較容易實現(xiàn)的,只需按某種線程調(diào)度算

法從所有處于就緒狀態(tài)的線程中選擇一個即可;剩余的問題一一舊線程的現(xiàn)場保護(hù)和新線程

的現(xiàn)場恢復(fù)、CPU控制權(quán)的轉(zhuǎn)移才是CPU調(diào)度的關(guān)鍵,它們是通過堆棧的切換來實現(xiàn)的。

在介紹堆棧切換的內(nèi)容之前,我們先來看看函數(shù)調(diào)用和進(jìn)行中斷處理時控制轉(zhuǎn)移的情況。

1.函數(shù)調(diào)用時的控制轉(zhuǎn)移情況

在執(zhí)行函數(shù)調(diào)用指令時,系統(tǒng)會自動地先將主調(diào)函數(shù)的下一條指令的地址(在CS:IP中)壓入堆棧,

然后把被調(diào)函數(shù)的入口地址裝入CS和IP寄存器(段內(nèi)函數(shù)調(diào)用只需壓入和裝配IP),控制就從主調(diào)函

數(shù)轉(zhuǎn)向被調(diào)函數(shù);當(dāng)執(zhí)行函數(shù)返回指令時,系統(tǒng)將當(dāng)前堆棧的棧頂?shù)膬蓚€字(主調(diào)函數(shù)下一條指令的地

址)彈出并送到IP和CS中(段內(nèi)函數(shù)返回只需彈出一個字送到IP中),控制就從被調(diào)函數(shù)返回到主調(diào)

函數(shù)。

例如,我們編寫了一個main()函數(shù)和一個fl()函數(shù),在main()中調(diào)用fl()。程序的設(shè)計

及調(diào)用返回關(guān)系如圖2-2所示:

voidfl(void);

inti;

main()

(函數(shù)調(diào)用進(jìn)fl()

i=0;入被調(diào)函數(shù)(

fl();-—Ai=2;

?1—1;■return;

返址1函數(shù)返回

})

圖2-2函數(shù)調(diào)用及返回圖

在執(zhí)行fl()函數(shù)的調(diào)用指令前的當(dāng)前堆棧的情況如圖2-3a所示。在執(zhí)行函數(shù)調(diào)用指令

時,系統(tǒng)首先將main()中函數(shù)調(diào)用語句的下一條指令即“i=l;”的地址(在CS:IP中,這

里用“返址1”表示)壓入堆棧,此時堆棧內(nèi)容如圖2-3b所示。然后將fl()函數(shù)的入口地址

裝入CS和IP寄存器,控制就從main。轉(zhuǎn)向fl()。當(dāng)執(zhí)行fl()的最后一條指令"return”(函

數(shù)返回指令)時,系統(tǒng)將前面保存在堆棧中的返址1的偏移和返址1的段址彈出并送到IP

和CS中,則控制就從fl()返回到main。了。

sp

返址1的偏移調(diào)用fl()

返址1的段址后的棧頂

從口()返回

后的棧頂

圖a調(diào)用fl()前的棧頂圖b調(diào)用f1()后的棧頂圖c從fl()返回后的棧頂

圖2-3函數(shù)調(diào)用前后堆棧內(nèi)容的變化

2.中斷處理時的控制轉(zhuǎn)移情況

除了函數(shù)調(diào)用以外,中斷也能實現(xiàn)控制的轉(zhuǎn)移。當(dāng)中斷發(fā)生時,系統(tǒng)首先將標(biāo)志寄存器

Flags的值壓入堆棧,然后將裝有被中斷程序下一條指令地址的CS和IP寄存器的內(nèi)容也分

別壓入堆棧,再從中斷向量中獲取中斷服務(wù)程序的入口地址并將它們裝入CS和IP寄存器,

這樣,控制就從被中斷的程序轉(zhuǎn)向中斷服務(wù)程序.中斷返回時,系統(tǒng)自動從棧頂彈出返址1

的偏移、返址1的段址和Flags并送到IP、CS和Flags寄存器中,CPU就開始繼續(xù)從斷點

處執(zhí)行被中斷程序。中斷處理前后堆棧內(nèi)容的變化情況如圖2-4所示:

sp

返址1的偏移進(jìn)入中斷處

返址1的段址理時的棧頂

中斷處理返

回后的棧頂

圖a中斷處理前的棧頂圖b進(jìn)入中斷處理時的棧頂圖c中斷處理返回后的棧頂

圖2-4中斷處理前后堆棧內(nèi)容的變化

3.Interrupt類型函數(shù)的特殊作用

在TurboC中提供了一個特殊的函數(shù)類型說明符interrupt,我們可利用它將一個函數(shù)申

明為中斷處理函數(shù)。例如我們寫了一個文件名為“aaa.c”的C程序,其內(nèi)容如下:

voidinterruptfun(void);

inti;

main()

(

i=0;

fun();

i=l;

)

voidinterruptfun(void)

(

i=2;

}

用編譯命令"tcc?Saaa”得到以上C程序的匯編碼:

ifndef??version

?debugmacro

endm

endif

?debugS"

_TEXTsegmentbytepublic'CODE,

DGROUPgroup_DATA,_BSS

assumecs:_TEXT,ds:DGROUP,ss:DGROUP

_TEXTends

_DATAsegmentwordpublic'DATA'

d@labelbyte

d@wlabelword

_DATAends

_BSSsegmentwordpublic'BSS'

b@labelbyte

b@wlabelword

?debugCE93B4F151F056161612E63

_BSSends

_TEXTsegmentbytepublic'CODE'

;?debugL3

_mainprocnear

;?debugL4

movwordptrDGROUP:_i,0

;?debugL5

pushf

callfarptr_fun

;?debugL6

movwordptrDGROUP:_i,l

@1:

;?debugL7

ret

_mainendp

;?debugL8

_funprocfar

pushax

pushbx

pushex

pushdx

pushes

pushds

pushsi

pushdi

pushbp

movbp,DGROUP

movds,bp

;?debugL10

movwordptrDGROUP:_i,2

@2:

;?debugL11

popbp

popdi

popsi

popds

popes

popdx

popex

popbx

popax

iret

_funendp

.TEXTends

_BSSsegmentwordpublic'BSS'

_ilabelword

db2dup(?)

_BSSends

?debugCE9

_DATAsegmentwordpublic'DATA,

s@labelbyte

_DATAends

_TEXTsegmentbytepublicCODE'

_TEXTends

public_main

public_fun

public_i

end

從編譯后的代碼中可以看出,對于fun()函數(shù),由于定義的類型是interrupt類型的中斷

處理函數(shù),所以在使用tcc命令進(jìn)行編譯時,編譯器將自動在fun()的開始加入一組push操

作(代碼中第二組黑體字部分),以保存被中斷程序的CPU現(xiàn)場環(huán)境信息;相對應(yīng)地在fun()

的代碼最后自動加入一組pop操作(代碼中第三組黑體字部分),以便中斷返回時恢復(fù)被中

斷程序的現(xiàn)場環(huán)境信息。

從編譯后的代碼段中還可以看出,main。對fun。的調(diào)用是通過:

pushf

callfarptr_fun/*代碼中的第一組黑體字部分*/

實現(xiàn)的,即先壓入Flags寄存器的內(nèi)容,再用call指令壓入返回地址,并轉(zhuǎn)去執(zhí)行fun()函數(shù)。

而在進(jìn)入fun()時,系統(tǒng)首先將執(zhí)行一組push操作,將AX、BX、CX、DX、ES、DS、SL

DLBP的值保存到堆棧中,再用fun()的數(shù)據(jù)段裝配DS寄存器,然后才執(zhí)行fun()中的具

體語句“i=2;”。而在返回前,先要執(zhí)行一組pop操作,從堆棧中恢復(fù)BP、DLSLDS、ES、

DX、CX、BX、AX的值,然后再用iret指令(而不是ret指令)返回,iret指令將從堆棧

中彈出返址的偏移、返址的段址、flags到CPU的IP、CS和Flags寄存器中,從而使CPU

繼續(xù)從斷點處執(zhí)行被中斷程序main()ofun()調(diào)用前后的堆棧內(nèi)容如圖2-5所示,圖中的“返

址1”是指main。函數(shù)中fun()函數(shù)調(diào)用指令的下一條指令“i=l”的地址。

BPsp

DIfun()執(zhí)行

SIpush操作后

DS

ES的棧頂

DX

CX

BX

AX

sp

返址1的偏移調(diào)用fun()返址1的偏移

返址1的段址時的棧頂返址1的段址

FlagsFlags

sp

調(diào)用fun()

前的棧頂

圖a調(diào)用fun()前的棧頂圖b調(diào)用fun()時的棧頂圖cfun()執(zhí)行push操作后

的棧頂

sp

返址1的偏移fun()執(zhí)行

pop操作后

返址1的段址

的棧頂

Flags

sp

從fun()返回

后的棧頂

圖dfun()執(zhí)行pop操作后圖e從fun()返回后的棧頂

的棧頂

圖2-5fun()調(diào)用前后的堆棧內(nèi)容的變化

4.利用堆棧切換實現(xiàn)CPU切換

從上面的描述可知,我們可以用函數(shù)或中斷服務(wù)子程序來處理CPU的切換,但關(guān)鍵仍

在于堆棧的切換。例如,系統(tǒng)中有兩個線程:線程1和線程2,它們的私有堆棧分別為stack1

和stack2;線程1目前擁有CPU,即線程1正在執(zhí)行,則系統(tǒng)的現(xiàn)行堆棧為線程1的私有堆

棧stack1;在線程1中調(diào)用一函數(shù)F(),函數(shù)的返回地址即線程1的下一條指令的地址將壓

入到現(xiàn)行堆棧stackl中;若在F()中,將系統(tǒng)的現(xiàn)行堆棧從stackl切換到stack2:保存現(xiàn)行

堆棧的段址SS和棧頂指針SP到變量ssl、spl中,并將線程2的堆棧stack2的段址和棧頂

指針裝到CPU的SS與SP寄存器中;如果stack2的棧頂有線程2的下一條指令的地址,則

從F()中返回時,將用現(xiàn)行堆棧stack2的棧頂?shù)淖盅b配IP和CS,CPU就開始執(zhí)行新線程,

即線程2。若再用保存在變量ssl、spl中的內(nèi)容來裝配SS和SP寄存器,即將現(xiàn)行堆棧切

換回線程1的私有堆棧,則CPU將被分配給線程1,它將從原來的斷點繼續(xù)往下執(zhí)行???/p>

慮到CPU切換時要進(jìn)行現(xiàn)場保護(hù),用中斷服務(wù)子程序來實現(xiàn)CPU的切換就顯得更方便。

下面我們用interrupt類型的函數(shù)swtch()來實現(xiàn)CPU在線程1和線程2間的切換,另外,

必須注意的是,在堆棧切換過程中一定要做到操作的原子性,這一點我們可以通過關(guān)中斷、

開中斷來達(dá)到。Swtch()的設(shè)計如下:

voidinterruptswtch(void)

(

disable();/*關(guān)中斷*/

/*保存現(xiàn)行堆棧的段址和棧頂指針供下次切換時用*/

ssl=_SS;/*ssl保存線程1的堆棧段址*/

spl=_SP;/*spl保存線程1的堆棧棧頂指針*/

/*切換堆棧*/

_SS=ss2;/*ss2是線程2的堆棧段址*/

_SP=sp2;/*ss2是線程2的堆棧的棧頂指針*/

enable();/*開中斷*/

)

上面代碼中用到的一SS,_SP是TurboC提供的偽變量。所謂的偽變量是一個和給定寄

存器相一致的簡單的標(biāo)識符,通過它們,我們可以在C語言程序中直接訪問相應(yīng)的寄存器。

表2-1中給出了TurboC可用的偽變量的完整列表、它們的類型、相應(yīng)的寄存器以及那些寄

存器通常的用處。

表2-1TurboC偽變量表

偽變量類型寄存器通常用處

_AX無符號整型AX通用/累加器

_AL無符號字符型ALAX的低字節(jié)

_AH無符號字符型AHAX的高字節(jié)

_BX無符號整型BX通用/變址器

_BL無符號字符型BLBX的低字節(jié)

_BH無符號字符型BHBX的高字節(jié)

_CX無符號整型CX通用/計數(shù)和循環(huán)

_CL無符號字符型CLCX的低字節(jié)

_CH無符號字符型CHCX的高字節(jié)

_DX無符號整型DX通用/存放數(shù)據(jù)

_DH無符號字符型DHDX的高字節(jié)

_CS無符號整型CS代碼段地址

_DS無符號整型DS數(shù)據(jù)段地址

_SS無符號整型SS堆棧段地址

_ES無符號整型ES附加段地址

_SP無符號整型SP堆棧棧頂指針(對SS

的偏移)

_BP無符號整型BP基址指針

_DI無符號整型DI用于寄存器變量

_S1無符號整型SI用于寄存器變量

由于swtch。是interrupt類型的函數(shù),因此編譯后的偽代碼如圖2-6b所示:

swtch()

圖a線程1的程序段圖bswtch()內(nèi)容圖c線程2的程序段

圖2-6堆棧切換過程中CPU控制的轉(zhuǎn)移情況

假設(shè)線程1對應(yīng)的程序段如圖2-6a所示,線程2對應(yīng)的程序段如圖2-6c所示。又假設(shè)

現(xiàn)在是線程1正在CPU上運(yùn)行,則當(dāng)前堆棧是線程1的堆棧stackl,其內(nèi)容如圖2-7a所示。

線程2目前處于就緒狀態(tài),其堆棧stack2的內(nèi)容如圖2?8a所示。

ssl:spl

BPswtch。執(zhí)行

D1

SIpush操作后

DS的棧頂

ES

DX

CX

BX

AX

地址1的偏移

地址1的段址

Flags

圖a線程1調(diào)用swtch()圖b線程1調(diào)用swtch()圖cswtch()執(zhí)行push

前的stackl的棧頂時的stackl的棧頂操作后stackl的內(nèi)容

圖2-7CPU切換過程中線程1堆棧stackl的變化情況

ss2:sp2

BP

DI

SI

DS原來保

ES存在棧

枝中的線

BX程2的現(xiàn)

AX場信息

地址2的偏移

地址2的段址

Flags

<

swtch()返回

后的棧頂

圖a線程2處于就緒狀圖bswtch()執(zhí)行pop圖cswtch()返回

態(tài)時的stack2的內(nèi)容操作后stack2的內(nèi)容后stack2的內(nèi)容

圖2-8CPU切換過程中線程2堆棧stack2的變化情況

下面我們來分析CPU從線程1切換到線程2的過程中控制的轉(zhuǎn)移情況以及堆棧的變化

情況。

(1)線程1執(zhí)行swtch()函數(shù)調(diào)用指令,系統(tǒng)首先將Flags、地址1的段址、地址1的

偏移壓棧,此時stackl的內(nèi)容如圖2-7b所示。

(2)然后CPU轉(zhuǎn)去執(zhí)行swtch(),首先執(zhí)行一組push操作,由于此時的當(dāng)前堆棧仍然

是線程1的stackl,因此push操作執(zhí)行完畢后stackl的內(nèi)容如圖2-7c所示。

(3)接下來swtch()進(jìn)行堆棧切換:首先保存線程1的堆棧stackl的段址和棧頂指針到

變量ssl和spl中;然后將線程2的堆棧stack2的段址和棧頂指針裝配到CPU的SS好SP

寄存器中,則從現(xiàn)在開始,系統(tǒng)的當(dāng)前堆棧已經(jīng)變成了stack2。

(4)接著swtch()執(zhí)行一組pop操作,將stack2中從BP開始到AX結(jié)束的所有內(nèi)容彈

出并裝入CPU的相應(yīng)寄存器中,此時stack2的內(nèi)容如圖2-8b所示。

(4)最后swtch()執(zhí)行中斷返回指令"iret”,從stack2中彈出線程2的偏移、線程2的

段址和Flags并送到CPU的IP.CS和FLAGS寄存器中,此時stack2的內(nèi)容如圖2-8c所示。

(5)CPU繼續(xù)執(zhí)行CS:IP寄存器所指向的指令,即線程2的地址2這個位置的指令。

于是CPU的控制權(quán)從線程1切換到線程2。

2.4.2DOS的不可重入性

溫馨提示

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

評論

0/150

提交評論