《計算機系統(tǒng)原理》 課件 第六章程序中IO操作的實現(xiàn)_第1頁
《計算機系統(tǒng)原理》 課件 第六章程序中IO操作的實現(xiàn)_第2頁
《計算機系統(tǒng)原理》 課件 第六章程序中IO操作的實現(xiàn)_第3頁
《計算機系統(tǒng)原理》 課件 第六章程序中IO操作的實現(xiàn)_第4頁
《計算機系統(tǒng)原理》 課件 第六章程序中IO操作的實現(xiàn)_第5頁
已閱讀5頁,還剩83頁未讀 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第六章程序中I/O操作的實現(xiàn)

用戶空間I/O軟件

內核空間I/O軟件

I/O硬件與軟件的接口

I/O和文件操作主要教學目標通過揭示高級語言程序中的I/O及文件操作請求的底層實現(xiàn)機制,使學生深刻理解OS在輸入/輸出系統(tǒng)中的重要作用;深刻理解計算機中硬件和軟件如何協(xié)調工作以完成計算機功能。主要教學內容I/O子系統(tǒng)的組成和層次結構用戶空間I/O軟件內核空間I/O軟件I/O硬件與軟件的接口I/O操作的實現(xiàn)分以下三個部分介紹第一講:用戶空間I/O軟件I/O子系統(tǒng)概述文件的基本概念用戶空間的I/O函數(shù)第二講:內核空間I/O軟件與設備無關的I/O軟件設備驅動程序中斷服務程序第三講:I/O硬件和軟件的接口I/O設備和系統(tǒng)互連I/O端口及其編址方式I/O模塊中的中斷系統(tǒng)I/O子系統(tǒng)概述所有高級語言的運行時(runtime)都提供了執(zhí)行I/O功能的機制例如,C語言中提供了包含像printf()和scanf()等這樣的標準I/O庫函數(shù),C++語言中提供了如<<(輸入)和>>(輸出)這樣的重載操作符。從高級語言程序中通過I/O函數(shù)或I/O操作符提出I/O請求,到設備響應并完成I/O請求,涉及到多層次I/O軟件和I/O硬件的協(xié)作。I/O子系統(tǒng)也采用層次結構從用戶I/O軟件切換到內核I/O軟件的唯一辦法是“異常”機制:系統(tǒng)調用(自陷)I/O子系統(tǒng)概述

各類用戶的I/O請求需要通過某種方式傳給OS:

最終用戶:鍵盤、鼠標通過操作界面?zhèn)鬟f給OS

用戶程序:通過函數(shù)(高級語言)轉換為系統(tǒng)調用傳遞給OSI/O軟件被組織成從高到低的四個層次,層次越低,則越接近設備而越遠離用戶程序。這四個層次依次為:

(1)用戶層I/O軟件(I/O庫函數(shù)調用系統(tǒng)級I/O函數(shù))

(2)與設備無關的操作系統(tǒng)I/O軟件

(3)設備驅動程序

(4)中斷服務程序

大部分I/O軟件都屬于操作系統(tǒng)內核態(tài)程序,最初的I/O請求在用戶程序中提出。OS在I/O系統(tǒng)中極其重要!OS用戶I/O軟件用戶軟件可用以下兩種方式提出I/O請求:(1)使用高級語言提供的標準I/O庫函數(shù)。例如,在C語言程序中可以直接使用像fopen、fread、fwrite和fclose等文件操作函數(shù),或printf、putc、scanf和getc等控制臺I/O函數(shù)。程序移植性很好!但是,使用標準I/O庫函數(shù)有以下幾個方面的不足:(a)標準I/O庫函數(shù)不能保證文件的安全性(無加/解鎖機制)(b)所有I/O都是同步的,程序必須等待I/O操作完成后才能繼續(xù)執(zhí)行(串行)

(c)有些I/O功能不適合甚至無法使用標準I/O庫函數(shù)實現(xiàn),如,不提供讀取文件元數(shù)據(jù)的函數(shù)(元數(shù)據(jù)包括文件大小和文件創(chuàng)建時間等)(d)用它進行網(wǎng)絡編程會造成易于出現(xiàn)緩沖區(qū)溢出等風險(2)使用OS提供的API函數(shù)或系統(tǒng)調用。例如,在Windows中直接使用像CreateFile、ReadFile、WriteFile、CloseHandle等文件操作API函數(shù),或ReadConsole、WriteConsole等控制臺I/O的API函數(shù)。對于Unix或Linux用戶程序,則直接使用像open、read、write、close等系統(tǒng)調用封裝函數(shù)。用戶I/O軟件用戶進程請求讀磁盤文件操作用戶進程使用標準C庫函數(shù)fread,或WindowsAPI函數(shù)

ReadFile,或Unix/Linux的系統(tǒng)調用函數(shù)read等要求讀一個磁盤文件塊。用戶程序中涉及I/O操作的函數(shù)最終會被轉換為一組與具體機器架構相關的指令序列,這里我們將其稱為I/O請求指令序列。例如,若用戶程序在IA-32架構上執(zhí)行,則I/O函數(shù)被轉換為IA-32的指令序列。每個指令系統(tǒng)中一定有一類陷阱指令(有些機器也稱為軟中斷指令或系統(tǒng)調用指令),主要功能是為操作系統(tǒng)提供靈活的系統(tǒng)調用機制。在I/O請求指令序列中,具體I/O請求被轉換為一條陷阱指令,在陷阱指令前面則是相應的系統(tǒng)調用參數(shù)的設置指令。IA-32/Linux的系統(tǒng)調用通常,系統(tǒng)調用被封裝成用戶程序能直接調用的函數(shù),如exit()、read()和open(),這些是標準C庫中系統(tǒng)調用對應的封裝函數(shù)。Linux中系統(tǒng)調用所用參數(shù)通過寄存器傳遞,傳遞參數(shù)的寄存器順序依次為:EAX(調用號)、EBX、ECX、EDX、ESI、EDI和EBP,除調用號以外,最多6個參數(shù)。封裝函數(shù)對應的機器級代碼有一個統(tǒng)一的結構:總是若干條傳送指令后跟一條陷阱指令。傳送指令用來傳遞系統(tǒng)調用的參數(shù),陷阱指令(如int$0x80)用來陷入內核進行處理。例如,若用戶程序調用系統(tǒng)調用write(1,“hello,world!\n”,14),將字符串“hello,world!\n”中14個字符顯示在標準輸出設備文件stdout上,則其封裝函數(shù)對應機器級代碼(用匯編指令表示)如下:

movl$4,%eax//調用號為4,送EAX movl$1,%ebx//標準輸出設備stdout的文件描述符為1,送EBXmovl$string,%ecx//字符串“hello,world!\n”首址送ECXmovl$14,%edx //字符串的長度為14,送EDXint$0x80 //系統(tǒng)調用系統(tǒng)I/O軟件OS在I/O子系統(tǒng)中的重要性由I/O系統(tǒng)以下三個特性決定:(1)共享性。I/O系統(tǒng)被多個程序共享,須由OS對I/O資源統(tǒng)一調度管理,以保證用戶程序只能訪問自己有權訪問的那部分I/O設備,并使系統(tǒng)的吞吐率達到最佳。(2)復雜性。I/O設備控制細節(jié)復雜,需OS提供專門的驅動程序進行控制,這樣可對用戶程序屏蔽設備控制的細節(jié)。(3)異步性。不同設備之間速度相差較大,因而,I/O設備與主機之間的信息交換使用異步的中斷I/O方式,中斷導致從用戶態(tài)向內核態(tài)轉移,因此必須由OS提供中斷服務程序來處理。那么,如何從用戶程序對應的用戶進程進入到操作系統(tǒng)內核執(zhí)行呢?系統(tǒng)調用!如:INT$0x80系統(tǒng)調用和APIOS提供一組系統(tǒng)調用,為用戶進程的I/O請求進行具體的I/O操作。應用編程接口(API)與系統(tǒng)調用兩者在概念上不完全相同,它們都是系統(tǒng)提供給用戶程序使用的編程接口,但前者指的是功能更廣泛、抽象程度更高的函數(shù),后者僅指通過軟中斷(自陷)指令向內核態(tài)發(fā)出特定服務請求的函數(shù)。系統(tǒng)調用封裝函數(shù)是API函數(shù)中的一種。API函數(shù)最終通過調用系統(tǒng)調用實現(xiàn)I/O。一個API可能調用多個系統(tǒng)調用,不同API可能會調用同一個系統(tǒng)調用。但是,并不是所有API都需要調用系統(tǒng)調用。從編程者來看,API和系統(tǒng)調用之間沒有什么差別。從內核設計者來看,API和系統(tǒng)調用差別很大。API在用戶態(tài)執(zhí)行,系統(tǒng)調用封裝函數(shù)也在用戶態(tài)執(zhí)行,但具體服務例程在內核態(tài)執(zhí)行。SKIPIA-32/Linux的系統(tǒng)調用系統(tǒng)調用(陷阱)是特殊異常事件,是OS為用戶程序提供服務的手段。Linux提供了幾百種系統(tǒng)調用,主要分為以下幾類:進程控制、文件操作、文件系統(tǒng)操作、系統(tǒng)控制、內存管理、網(wǎng)絡管理、用戶管理、進程通信等系統(tǒng)調用號是系統(tǒng)調用跳轉表索引值,跳轉表給出系統(tǒng)調用服務例程首址BACK系統(tǒng)調用及其參數(shù)傳遞在用戶態(tài),當進程調用一個系統(tǒng)調用時,CPU切換到內核態(tài),并開始執(zhí)行一個被稱為系統(tǒng)調用處理程序的內核函數(shù)例如,IA-32中,可以通過兩種方式調用Linux的系統(tǒng)調用執(zhí)行軟中斷指令int$0x80執(zhí)行指令sysenter(老的x86不支持該指令)內核實現(xiàn)了許多系統(tǒng)調用,因此,用一個系統(tǒng)調用號(存放在EAX中)來標識不同的系統(tǒng)調用除了調用號以外,系統(tǒng)調用還需要其他參數(shù),不同系統(tǒng)調用所需參數(shù)的個數(shù)和含義不同,輸入?yún)?shù)通過通用寄存器傳遞,若參數(shù)個數(shù)超出寄存器個數(shù),則將需傳遞參數(shù)塊所在內存區(qū)首址放在寄存器中傳遞(除調用號以外,最多6個參數(shù))傳遞參數(shù)的寄存器順序:EAX(系統(tǒng)調用號)、EBX、ECX、EDX、ESI、EDI和EBP返回參數(shù)為整數(shù)值。正數(shù)或0表示成功,負數(shù)表示出錯碼用戶程序、C庫函數(shù)和內核用戶程序總是通過某種I/O函數(shù)或I/O操作符請求I/O操作。

例如,讀一個磁盤文件記錄時,可調用C標準I/O庫函數(shù)fread(),也可直接調用系統(tǒng)調用封裝函數(shù)read()來提出I/O請求。不管是C庫函數(shù)、API函數(shù)還是系統(tǒng)調用封裝函數(shù),最終都通過操作系統(tǒng)內核提供的系統(tǒng)調用來實現(xiàn)I/O。printf()函數(shù)的調用過程如下:

Linux系統(tǒng)中printf()函數(shù)的執(zhí)行過程某函數(shù)調用了printf(),執(zhí)行到調用printf()語句時,便會轉到C語言I/O標準庫函數(shù)printf()去執(zhí)行;printf()通過一系列函數(shù)調用,最終會調用函數(shù)write();調用write()時,便會通過一系列步驟在內核空間中找到write對應的系統(tǒng)調用服務例程sys_write來執(zhí)行。main(){…printf();…}用戶程序

printf(){…xxxx();…}system_call(){…xxxx();…}系統(tǒng)調用封裝函數(shù)系統(tǒng)調用處理程序用戶空間、運行在用戶態(tài)

內核空間、運行在內核態(tài)

write()

{…int$0x80…}I/O標準庫函數(shù)sys_write(){………}系統(tǒng)調用服務例程在system_call中如何知道要轉到sys_write執(zhí)行呢?根據(jù)系統(tǒng)調用號!Linux系統(tǒng)下的write()封裝函數(shù)1write:2pushl%ebx //將EBX入棧(EBX為被調用者保存寄存器)3movl$4,%eax //將系統(tǒng)調用號4送EAX

4movl8(%esp),%ebx //將文件描述符fd送EBX5movl12(%esp),%ecx //將所寫字符串首址buf送ECX6movl16(%esp),%edx //將所寫字符個數(shù)n送EDX7int $0x80 //進入系統(tǒng)調用處理程序system_call執(zhí)行8cmpl$-125,%eax //檢查返回值(所有正數(shù)都小于FFFFFF83H)9jbe.L1 //若無錯誤,則跳轉至.L1(按無符號數(shù)比)10negl%eax //將返回值取負送EAX11movl%eax,error //將EAX的值送error12movl$-1,%eax //將write函數(shù)返回值置-113.L1: 14popl%ebx15ret用法:ssize_twrite(intfd,constvoid*buf,size_tn);size_t和ssize_t分別是unsignedint和int,因為返回值可能是-1。內核執(zhí)行write的結果在EAX中返回,正確時為所寫字符數(shù)(最高位為0),出錯時為錯誤碼的負數(shù)(最高位為1)在Linux內核中單向調用20次以上readsys_readInt0x80觸發(fā)系統(tǒng)調用與設備無關層設備驅動層文件系統(tǒng)層通用塊設備層I/O調度層用戶空間中的I/O函數(shù)用戶程序可通過調用特定的I/O函數(shù)的方式提出I/O請求。在UNIX/Linux系統(tǒng)中,可以是C標準I/O庫函數(shù)或系統(tǒng)調用的封裝函數(shù),前者如文件I/O函數(shù)fopen()、fread()、fwrite()和fclose()或控制臺I/O函數(shù)printf()、putc()、scanf()和getc()等;后者如open()、read()、write()和close()等。標準I/O庫函數(shù)比系統(tǒng)調用封裝函數(shù)抽象層次高,后者屬于系統(tǒng)級I/O函數(shù)。與系統(tǒng)提供的API函數(shù)一樣,前者是基于后者實現(xiàn)的。

用戶程序中的I/O函數(shù)文件的基本概念所有I/O操作通過讀寫文件實現(xiàn),所有外設,包括網(wǎng)絡、終端設備,都被看成文件。所有物理設備抽象成邏輯上統(tǒng)一的“文件”使得用戶程序訪問物理設備與訪問真正的磁盤文件完全一致。例如,fprintf/fwrite(主要是磁盤文件)和printf(stdout)都通過統(tǒng)一的write函數(shù)陷入內核,差別則由內核處理!

UNIX系統(tǒng)中,文件就是一個字節(jié)序列。通常,將鍵盤和顯示器構成的設備稱為終端(terminal),對應標準輸入、和標準(錯誤)輸出文件;像磁盤、光盤等外存上的文件則是普通文件。根據(jù)文件的可讀性,文件被分成ASCII文件和二進制文件兩類。ASCII文件也稱文本文件,可由多個正文行組成,每行以換行符‘\n’結束,每個字符占一個字節(jié)。標準輸入和標準(錯誤)輸出文件是ASCII文件。普通文件可能是文本文件或二進制文件。

問題:.c、.cpp、.o、.txt、.exe文件各是什么類型文件?哪里遇過“文件”?intfprintf(FILE*fp,char*format,[argument])printf在哪顯示信息?stdout文件!即終端顯示器TTYStream!字節(jié)流文件的創(chuàng)建和打開

讀寫文件前,用戶程序須告知將對文件進行何種操作:讀、寫、添加還是可讀可寫,通過打開或創(chuàng)建一個文件來實現(xiàn)。已存在的文件:可直接打開不存在的文件:則先創(chuàng)建創(chuàng)建文件:intcreat(char*name,mode_tperms);創(chuàng)建新文件時,應指定文件名和訪問權限,系統(tǒng)返回一個非負整數(shù),它被稱為文件描述符fd(filedescriptor)。文件描述符用于標識被創(chuàng)建的文件,在以后對文件的讀寫等操作時用文件描述符代表文件。

2.打開文件:intopen(char*name,intflags,mode_tperms);標準輸入(fd=0)、標準輸出(fd=1)和標準錯誤(fd=2)三種文件自動打開,其他文件須用creat或open函數(shù)顯式創(chuàng)建或打開后才能讀寫參數(shù)perms用于指定文件的訪問權限,通常在open函數(shù)中該參數(shù)總是0,除非以創(chuàng)建方式打開,此時,參數(shù)flags中應帶有O_CREAT標志。參數(shù)flags:O_RDONLY,O_WRONLY|O_APPEND,O_RDWR等

例:fd=open(“test.txt”,O_RDONLY,0);文件的讀/寫3.讀文件:ssize_tread(intfd,void*buf,size_tn);將fd中當前位置k開始的n個字節(jié)讀到buf中,讀后當前位置為k+n。若文件長度為m,當k+n>m時,則讀取字節(jié)數(shù)為m-k<n,讀后當前位置為文件尾。返回實際字節(jié)數(shù),當m=k(EOF)時,返回值為0。4.寫文件:ssize_twrite(intfd,constvoid*buf,size_tn);將buf中n字節(jié)寫到fd中,從當前位置k處開始寫。返回實際寫入字節(jié)數(shù)m,寫后當前位置為k+m。對于普通文件,實際字節(jié)數(shù)等于n。對于read和write系統(tǒng)調用,可以一次讀/寫任意個字節(jié)。顯然,按一個物理塊大小讀/寫較好,可減少系統(tǒng)調用次數(shù)。有些情況下,真正讀/寫字節(jié)數(shù)比設定所需字節(jié)數(shù)少,這并不是一種錯誤。在讀/寫磁盤文件時,除非遇到EOF,否則不會出現(xiàn)這種情況。但當讀/寫的是終端設備或網(wǎng)絡套接字文件、UNIX管道、Web服務器等都可能出現(xiàn)這種情況。是不帶緩沖的讀寫:直接從(向)磁盤讀(寫),沒有緩沖文件的定位和關閉5.設置讀寫位置:longlseek(intfd,longoffset,intorigin);offset指出相對字節(jié)數(shù)origin指出基準:開頭(0)、當前位置(1)和末尾(2)

例:lseek(fd,5L,0);表示定位到文件開始后的第5字節(jié)lseek(fd,0L,2);表示定位到文件末尾返回的是位置值,若發(fā)生錯誤,則返回-16.元數(shù)據(jù)統(tǒng)計:intstat(const*name,structstat*buf);intfstat(intfd,structstat*buf);文件的所有屬性信息,包括:文件描述符、文件名、文件大小、創(chuàng)建時間、當前讀寫位置等,由內核維護,稱為文件的元數(shù)據(jù)(metadata)。用戶程序可通過stat()或fstat()函數(shù)查看文件元數(shù)據(jù)。stat第一個參數(shù)是文件名,而fstat指出的是文件描述符,除第一個參數(shù)類型不同外,其他全部一樣。7.關閉文件:close(intfd);典型的stdio.h的部分內容C標準I/O庫函數(shù)基于系統(tǒng)調用實現(xiàn)C標準I/O庫函數(shù)將打開文件抽象為一個類型為FILE的“流”,它在stdio.h中定義。FILE_iob[OPEN_MAX]={{0,(char*)0,(char*)0,_READ,0},{0,(char*)0,(char*)0,_WRITE,1},{0,(char*)0,(char*)0,_WRITE|_UNBUF,2},};stdout和stderr都用于輸出,但是,stderr為非緩存stdout為帶緩存#define NULL 0#define EOF (-1)#define BUFSIZ 1024 #define OPEN_MAX20/*最多打開文件數(shù)*/typedef struct_iobuf { intcnt;/*未讀寫字節(jié)數(shù)*/ char*ptr;/*下一可讀寫位置*/ char*base;/*起始位置*/ intflag;/*存取模式*/ intfd; /*文件描述符*/}FILE;externFILE_iob[OPEN_MAX];

#define stdin (&_iob[0])#define stdout (&_iob[1])#define stderr (&_iob[2])enum_flags{ _READ=01,/*fileopenforreading*/ _WRITE=02,/*fileopenforwriting*/ _UNBUF=04,/*fileisunbuffered*/ _EOF=010,/*EOFhasoccurredonthisfile*/ _ERR=020/*erroroccurredonthisfile*/};用數(shù)組實現(xiàn)I/O(文件)緩沖帶緩沖有何好處?帶緩沖I/O的實現(xiàn)從文件fp中讀數(shù)據(jù)時,F(xiàn)ILE中定義的緩沖區(qū)為輸入流緩沖(在內存)首先要從文件fp中讀入1024(緩沖大小BUFSIZ=1024)個字節(jié)數(shù)據(jù)到緩存,然后,再按需從緩存中讀取1個(如getc)或n個(如fread)字節(jié)并返回

未讀部分

已讀部分輸入流緩沖(fp)->base(fp)->ptr(fp)->cnt未讀部分已讀部分已讀入并出緩沖文件中未緩存文件當前指針輸入流緩沖fp文件對應的字節(jié)流fp文件在哪里?磁盤上或鍵盤輸入用FILE結構描述緩沖區(qū)的起始位置輸入預先讀入緩沖區(qū)帶緩沖I/O的實現(xiàn)向文件fp中寫數(shù)據(jù)時,F(xiàn)ILE中定義的緩沖區(qū)為輸出流緩沖先按需不斷地向緩存寫1個(如putc)或n個(如fwrite)字節(jié),遇到換行符\n或緩存被寫滿1024(緩沖大小BUFSIZ=1024)個字節(jié),則將緩存內容一次寫入文件fp中

未寫部分

已寫部分輸出流緩沖(fp)->base(fp)->ptr(fp)->cnt未寫部分已寫部分已出緩沖并寫入fp文件當前指針輸出流緩沖fp文件對應的字節(jié)流輸出一次性寫回文件中帶緩沖I/O的實現(xiàn)輸出流緩沖屬性有三種:全緩沖_IOFBF(fullybuffered)行緩沖_IOLBF(linebuffered)

非緩沖_IO_NBF(nobuffering)普通文件為全緩沖,即使遇到換行符也不會寫文件,只有當緩沖區(qū)滿時才會將緩沖區(qū)內容寫入fd中stdout對應是行緩沖,遇到換行符/n時,也會寫文件stderr對應的是非緩沖,直接寫文件,無需緩沖stdout和stderr的差別猜一下在Linux中以下程序輸出什么?#include<stdio.h>

int

main()

{

fprintf(stdout,

“hello");

fprintf(stderr,

“world!");

return

0;

}

輸出結果為:world!hello#include<stdio.h>

int

main()

{

fprintf(stdout,

“hello");

fprintf(stderr,

“world!\n");

return

0;

}

輸出結果為:world!hello#include<stdio.h>

int

main()

{

fprintf(stdout,

“hello\n");

fprintf(stderr,

“world!");

return

0;

}

輸出結果為:helloworld!stdout和stderr都用于標準輸出,但是,stderr為_WRITE|_UNBUFstdout為_WRITE有緩沖:遇到換行符\n或緩沖滿(BUFSIZE=1024)才寫文件!stdout和stderr的差別例子(可執(zhí)行文件為hello)#include

<stdio.h>

void

main()

{

fprintf(stdout,

"from

stdout\n");

fprintf(stderr,

"from

stderr\n");

}二者都默認指向標準輸出,即顯示器;也都可重定位到普通文件中!./hello>out.txt:stdout送out.txt,stderr送屏幕

./hello2>err.txt:stdout送屏幕,stderr送err.txt

./hello>out.txt2>err.txt:stdout送out.txt,stderr送err.txt

./hello>combine.txt2>&1:stdout和stderr都送combine.txt

./hello>combine.txt2>combine.txt:

stdout和stderr都送combine.txt

執(zhí)行結果如下:stdio.h中更多的定義在stdio.h中,還有feof()、ferror()、fileno()、getc()、putc()、getchar()、putchar()等宏定義。系統(tǒng)級I/O函數(shù)對文件的標識是文件描述符,C標準I/O庫函數(shù)中對文件的標識是指向FILE結構的指針,F(xiàn)ILE中定義了1024字節(jié)的流緩沖區(qū)。使用流緩沖區(qū)可使文件內容緩存在用戶緩沖區(qū)中,而不是每次都直接讀/寫文件,從而減少執(zhí)行系統(tǒng)調用次數(shù)。int_fillbuf(FILE*);/*第一次調用getc(),需用_fillbuf()填充緩沖區(qū)*/int_flushbuf(int,FILE*);/*遇換行或寫緩沖區(qū)滿,調用其將緩沖內容寫文件*/#definefeof(p)(((p)->flag&_EOF)!=0)#defineferror(p)(((p)->flag&_ERR)!=0)#definefileno(p)((p)->fd)#define

getc(p)

(--(p)->cnt>=0?(unsignedchar)*(p)->ptr++:_fillbuf(p))#defineputc(x,p)

(--(p)->cnt>=0?*(p)->ptr++=(x):_flushbuf((x),p))

#definegetchar()getc(stdin)#defineputchar(x)putc((x),stdout)輸入緩沖內容未讀完。cnt為未讀字符數(shù)。調用_fillbuf()后,cnt<=1023。輸出緩沖未寫滿。cnt為可寫字符數(shù)。調用_flushbuf()后,cnt=1024-1=1023。系統(tǒng)調用的開銷很大!SKIP文件的創(chuàng)建和打開

讀寫文件前,用戶程序須告知將對文件進行何種操作:讀、寫、添加還是可讀可寫,通過打開或創(chuàng)建一個文件來實現(xiàn)。已存在的文件:可直接打開不存在的文件:則先創(chuàng)建創(chuàng)建文件:intcreat(char*name,mode_tperms);創(chuàng)建新文件時,應指定文件名和訪問權限,系統(tǒng)返回一個非負整數(shù),它被稱為文件描述符fd(filedescriptor)。文件描述符用于標識被創(chuàng)建的文件,在以后對文件的讀寫等操作時用文件描述符代表文件。

2.打開文件:intopen(char*name,intflags,mode_tperms);標準輸入(fd=0)、標準輸出(fd=1)和標準錯誤(fd=2)三種文件自動打開,其他文件須用creat或open函數(shù)顯式創(chuàng)建或打開后才能讀寫參數(shù)perms用于指定文件的訪問權限,通常在open函數(shù)中該參數(shù)總是0,除非以創(chuàng)建方式打開,此時,參數(shù)flags中應帶有O_CREAT標志。參數(shù)flags:O_RDONLY,O_WRONLY|O_APPEND,O_RDWR等

例:fd=open(“test.txt”,O_RDONLY,0);BACK_fillbuf()函數(shù)的實現(xiàn)#include“syscalls.h”/*_fillbuf:allocateandfillinputbuffer*/int_fillbuf(FILE*fp){intbufsize;if((fp->flag&(_READ|_EOF|_ERR))!=_READ)returnEOF;

bufsize=(fp->flag&_UNBUF)?1:BUFSIZ;if((fp->base==NULL) /*剛開始,還沒有申請緩沖*/if((fp->base=(char*)malloc(bufsize))==NULL) returnEOF; /*緩沖沒有申請到*/fp->ptr=fp->base;fp->cnt=read(fp->fd,fp->ptr,bufsize);/*cnt<=1024*/if(--fp->cnt<0){ /*cnt=0*/if(fp->cnt==-1)fp->flag|=_EOF;elsefp->flag|=_ERR;fp->cnt=0;returnEOF;}return(unsignedchar)*fp->ptr++;/*0<cnt<=1023*/}stderr沒有緩沖即bufsize=1調用系統(tǒng)調用封裝函數(shù)進行讀文件操作,一次將輸入緩沖讀滿cnt減1返回緩沖區(qū)當前字節(jié),并ptr加1int_flushbuf(intx,FILE*fp)

{

unsignednc;

intbufsize;

if(fp<_iob||fp>_iob+OPEN_MAX)

returnEOF;

if((fp->flag&(_WRITE|_ERR))!=_WRITE)

returnEOF;

bufsize=(fp->flag&_UNBUF)?1:BUFSIZ;

if(fp->base==NULL){/*剛開始,還沒有申請緩沖*/

if((fp->base=(char*)malloc(bufsize))==NULL){

fp->flag|=_ERR;

returnEOF;

}

}else{/*已存在緩沖,且遇到換行符或緩沖已滿*/

nc=fp->ptr-fp->base;

if(write(fp->fd,fp->base,nc)!=nc){

fp->flag|=_ERR;

returnEOF;

}

}

fp->ptr=fp->base;

*fp->ptr++=x;

fp->cnt=bufsize-1;

returnx;}_flushbuf()函數(shù)的實現(xiàn)舉例:文件復制功能的實現(xiàn)/*方式一:getc/putc版本*/voidfilecopy(FILE*infp,FILE*outfp){ intc; while((c=getc(infp))!=EOF) putc(c,outfp);}/*方式二:read/write版本*/voidfilecopy(FILE*infp,FILE*outfp){ charc; while(read(infp->fd,&c,1)!=0) write(outfp->fd,&c,1);}哪種方式更好?方式一更好!Why?因其系統(tǒng)調用次數(shù)少!對于方式二,若文件長度為n,則需執(zhí)行2n次系統(tǒng)調用;對于方式一,若文件長度為n,則執(zhí)行系統(tǒng)調用的次數(shù)約為n/512。還有其他的實現(xiàn)方式嗎?使用fread()和fwrite()使用fgetc()和fputc()使用WindowsAPI函數(shù)CopyFile()

為何要盡量減少系統(tǒng)調用次數(shù)?系統(tǒng)調用的開銷有多大?實現(xiàn)一個功能有多種方式,但開銷和性能不同,需要權衡!相當大!SKIP以hello程序為例說明#include<stdio.h>intmain(){printf("hello,world\n");}假定以下用戶程序對應的進程為pmain(){…printf();…}用戶程序

printf(){…xxxx();…}system_call(){…xxxx();…}系統(tǒng)調用封裝函數(shù)系統(tǒng)調用處理程序用戶空間、運行在用戶態(tài)

內核空間、運行在內核態(tài)

write()

{…int$0x80…}I/O標準庫函數(shù)sys_write(){………}系統(tǒng)調用服務例程字符串輸出最終是由內核中的sys_write系統(tǒng)調用服務例程實現(xiàn)Linux系統(tǒng)下的write()封裝函數(shù)1write:2pushl%ebx //將EBX入棧(EBX為被調用者保存寄存器)3movl$4,%eax //將系統(tǒng)調用號4送EAX

4movl8(%esp),%ebx //將文件描述符fd送EBX5movl12(%esp),%ecx //將所寫字符串首址buf送ECX6movl16(%esp),%edx //將所寫字符個數(shù)n送EDX7int $0x80 //進入系統(tǒng)調用處理程序system_call執(zhí)行8cmpl$-125,%eax //檢查返回值9jbe.L1 //若無錯誤,則跳轉至.L1(按無符號數(shù)比)10negl%eax //將返回值取負送EAX11movl%eax,error //將EAX的值送error12movl$-1,%eax //將write函數(shù)返回值置-113.L1: 14popl%ebx15ret用法:ssize_twrite(intfd,constvoid*buf,size_tn);size_t和ssize_t分別是unsignedint和int,因為返回值可能是-1。內核執(zhí)行write的結果在EAX中返回,正確時為所寫字符數(shù)(最高位為0),出錯時為錯誤碼的負數(shù)(最高位為1)在Linux內核中單向調用20次以上readsys_readInt0x80觸發(fā)系統(tǒng)調用與設備無關層設備驅動層文件系統(tǒng)層通用塊設備層I/O調度層BACKI/O操作的實現(xiàn)分以下三個部分介紹第一講:用戶空間I/O軟件I/O子系統(tǒng)概述文件的基本概念用戶空間的I/O函數(shù)第二講:內核空間I/O軟件與設備無關的I/O軟件設備驅動程序中斷服務程序第三講:I/O硬件和軟件的接口I/O設備和系統(tǒng)互連I/O端口及其編址方式I/O模塊中的中斷系統(tǒng)設備無關I/O軟件層設備驅動程序統(tǒng)一接口操作系統(tǒng)為所有外設的設備驅動程序規(guī)定一個統(tǒng)一接口,這樣,新設備的驅動程序只要按統(tǒng)一接口規(guī)范來編制,就可在不修改操作系統(tǒng)的情況下,添加新設備驅動程序并使用新的外設進行I/O。所有設備都抽象成文件,設備名和文件名在形式上沒有差別,設備和文件具有統(tǒng)一的接口,不同設備名和文件名被映射到對應設備驅動程序。緩沖處理每個設備的I/O都需使用內核緩沖區(qū),因而緩沖區(qū)的申請和管理等處理是所有設備公共的,可包含在與設備無關的I/O軟件部分錯誤報告I/O操作在內核態(tài)執(zhí)行時所發(fā)生的錯誤信息,都通過與設備無關的I/O軟件返回給用戶進程,也即:錯誤處理框架與設備無關。直接返回編程等錯誤,無需設備驅動程序處理,如,請求了不可能的I/O操作;寫信息到一個輸入設備或從一個輸出設備讀信息;指定了一個無效緩沖區(qū)地址或者參數(shù);指定了不存在的設備等。有些錯誤由設備驅動程序檢測出來并處理,若驅動程序無法處理,則將錯誤信息返回給設備無關I/O軟件,再由設備無關I/O軟件返回給用戶進程,如寫一個已被破壞的磁盤扇區(qū);打印機缺紙;讀一個已關閉的設備等。設備無關I/O軟件層打開與關閉文件對設備或文件進行打開或關閉等I/O函數(shù)所對應的系統(tǒng)調用,并不涉及具體的I/O操作,只要直接對主存中的一些數(shù)據(jù)結構進行修改即可,這部分工作也由設備無關軟件來處理。邏輯塊大小處理

為了為所有的塊設備和所有的字符設備分別提供一個統(tǒng)一的抽象視圖,以隱藏不同塊設備或不同字符設備之間的差異,與設備無關的I/O軟件為所有塊設備或所有字符設備設置統(tǒng)一的邏輯塊大小。對于塊設備,不管磁盤扇區(qū)和光盤扇區(qū)有多大,所有邏輯數(shù)據(jù)塊的大小相同,這樣,高層I/O軟件就只需處理簡化的抽象設備,從而在高層軟件中簡化了數(shù)據(jù)定位等處理。驅動程序與I/O指令控制外設進行輸入/輸出的底層I/O軟件是驅動程序驅動程序設計者應了解設備控制器及設備的工作原理,包括:設備控制器中有哪些用戶可訪問的寄存器、控制/狀態(tài)寄存器中每一位的含義、設備控制器與外設之間的通信協(xié)議等,而關于外設的機械特性,程序員則無需了解。驅動程序通過訪問I/O端口控制外設進行I/O:將控制命令送到控制寄存器來啟動外設工作;讀取狀態(tài)寄存器了解外設和設備控制器的狀態(tài);訪問數(shù)據(jù)緩沖寄存器進行數(shù)據(jù)的輸入和輸出。對I/O端口的訪問操作由I/O指令完成,它們是一種特權指令IA-32中的I/O指令:in、ins、out和outsin和ins用于將I/O端口的內容取到CPU內的通用寄存器中;out和outs用于將通用寄存器內容輸出到I/O端口。

如INAL,DX:DX中存放I/O端口地址,將I/O端口中的內容取到AL中設備驅動程序每個外設具體的I/O操作需通過執(zhí)行設備驅動程序來完成外設種類繁多、其控制接口不一,導致不同外設的設備驅動程序千差萬別,因而設備驅動程序與設備相關每個外設或每類外設都有一個設備控制器,其中包含各種I/O端口。CPU通過執(zhí)行設備驅動程序中的I/O指令訪問各種I/O端口設備所采用的I/O控制方式不同,驅動程序的實現(xiàn)方式也不同程序直接控制:驅動程序完成用戶程序的I/O請求后才結束。這種情況下,用戶進程在I/O過程中不會被阻塞,內核空間的I/O軟件一直代表用戶進程在內核態(tài)進行I/O處理

。(干等!)中斷控制:驅動程序啟動第一次I/O操作后,將調出其他進程執(zhí)行,而當前用戶進程被阻塞。在CPU執(zhí)行其他進程的同時,外設進行I/O操作,此時,CPU和外設并行工作。外設完成I/O時,向CPU發(fā)中斷請求,然后CPU調出相應中斷服務程序執(zhí)行。在中斷服務程序中再次啟動I/O操作。DMA控制:驅動程序對DMA控制器初始化后,便發(fā)送“啟動DMA傳送”命令,外設開始進行I/O操作并在外設和主存間傳送數(shù)據(jù)。同時CPU執(zhí)行處理器調度程序,轉其他進程執(zhí)行,當前用戶進程被阻塞。DMA控制器完成所有I/O任務后,向CPU發(fā)送一個“DMA完成”中斷請求信號。三種基本I/O方式程序直接控制方式(最簡單的I/O方式)無條件傳送:對簡單外設定時(同步)進行數(shù)據(jù)傳送條件傳送:CPU主動查詢,也稱程序查詢或輪詢(Polling)方式I/OInterrupt(中斷I/O方式):幾乎所有系統(tǒng)都支持中斷I/O方式若一個I/O設備需要CPU干預,它就通過中斷請求通知CPUCPU中止當前程序的執(zhí)行,調出OS(中斷處理程序)來執(zhí)行處理結束后,再返回到被中止的程序繼續(xù)執(zhí)行DirectMemoryAccess(DMA方式):磁盤等高速外設所用的方式磁盤等高速外設成批地直接和主存進行數(shù)據(jù)交換需要專門的DMA控制器控制總線,完成數(shù)據(jù)傳送數(shù)據(jù)傳送過程無需CPU參與以hello程序為例說明#include<stdio.h>intmain(){printf("hello,world\n");}假定以下用戶程序對應的進程為pmain(){…printf();…}用戶程序

printf(){…xxxx();…}system_call(){…xxxx();…}系統(tǒng)調用封裝函數(shù)系統(tǒng)調用處理程序用戶空間、運行在用戶態(tài)

內核空間、運行在內核態(tài)

write()

{…int$0x80…}I/O標準庫函數(shù)sys_write(){………}系統(tǒng)調用服務例程字符串輸出最終是由內核中的sys_write系統(tǒng)調用服務例程實現(xiàn)sys_write可用三種I/O方式實現(xiàn):程序查詢、中斷和DMA程序查詢(Polling)方式I/O設備(包括設備控制器)將自己的狀態(tài)放到狀態(tài)寄存器中打印缺紙、打印機忙、未就緒等都是狀態(tài)OS階段性地查詢狀態(tài)寄存器中的特定狀態(tài),以決定下一步動作如:未“就緒”時,則一直“等待”例如:sys_write進行字符串打印的程序段大致過程如下:copy_string_to_kernel(strbuf,kernelbuf,n);//將字符串復制到內核緩沖區(qū)for(i=0;i<n;i++){ //對于每個打印字符循環(huán)執(zhí)行

while(printer_status!=READY); //等待直到打印機狀態(tài)為“就緒”*printer_data_port=kernelbuf[i]; //向數(shù)據(jù)端口輸出一個字符*printer_control_port=START; //發(fā)送“啟動打印”命令}return_to_user(); //返回用戶態(tài)如何判斷“就緒”?如何“等待”?讀取狀態(tài)寄存器,判斷特定位(1-就緒;0-未就緒)是否為1等待:讀狀態(tài)、判斷是否為1;不是,則繼續(xù)讀狀態(tài)、判斷、…….程序查詢(Polling)方式舉例:控制打印輸出設置計數(shù)值讀接口狀態(tài)輸出一個字符就緒否啟動打印修改內存地址修改計數(shù)值完成否結束NYNYN設置內存緩沖區(qū)首址這里“就緒”的含義是什么?打印機控制器的數(shù)據(jù)緩沖中內容已被取走,現(xiàn)為“空”,可接受新的打印字符。由打印控制器自動設置功能:打印AL寄存器中的字符。PRINT PROC NEAR PUSH AX;保留用到的寄存器

PUSH DX ;保留用到的寄存器

MOV DX,378H;數(shù)據(jù)鎖存器口地址送DX

OUT DX,AL ;輸出要打印的字符到數(shù)據(jù)鎖存器

MOVDX,379H;狀態(tài)寄存器口地址送DXWAIT: IN AL,DX;讀打印機狀態(tài)位

TEST AL,80H;檢查忙位

JE WAIT ;等待直到打印機不忙

MOVDX,37AH;命令(控制)寄存器口地址送DX

MOV AL,0DH;置選通位=1(表示啟動打印)

OUT DX,AL ;使命令寄存器中選通位置1 POP DX POP AX;恢復寄存器

RETPRINT ENDP打印輸出標準子程序回顧:過程/函數(shù)/子程序中的開始總是先要保護現(xiàn)場,最后總是要恢復現(xiàn)場!SKIP程序查詢I/O方式特點:簡單、易控制、外圍接口控制邏輯少;CPU與外設串行工作,效率低、速度慢,適合于慢速設備查詢開銷極大(CPU完全在等待“外設完成”)工作方式:完全串行或部分串行,CPU用100%的時間為I/O服務!外設CPU啟動探詢完成啟動探詢完成“踏步”現(xiàn)象工作工作“探詢”期間,可一直不斷查詢(獨占查詢),也可定時查詢(需保證數(shù)據(jù)不丟失!)。此時,CPU處于停止狀態(tài)嗎?不是!只是不斷執(zhí)行“IN-TEST-JE”3條指令,稱為“忙等待”!sys_write系統(tǒng)調用服務例程響應中斷I/O方式基本思想:

當外設準備好(ready)時,便向CPU發(fā)中斷請求,CPU響應后,中止現(xiàn)行程序的執(zhí)行,轉入“中斷服務程序”進行輸入/出操作,以實現(xiàn)主機和外設接口之間的數(shù)據(jù)傳送,并啟動外設工作。“中斷服務程序”執(zhí)行完后,返回原被中止的程序斷點處繼續(xù)執(zhí)行。此時,外設和CPU并行工作。外設CPU啟動完成啟動完成工作工作工作請求啟動請求響應sys_write系統(tǒng)調用服務例程返回上述哪段時間CPU和外設并行工作?程序切換(響應中斷)由硬件完成,即執(zhí)行“中斷隱指令”,時間為中斷服務程序P被阻塞,調其他進程Q執(zhí)行QQQP中斷I/O方式copy_string_to_kernel(strbuf,kernelbuf,n);//將字符串復制到內核緩沖區(qū)enable_interrupts(); //開中斷,允許外設發(fā)出中斷請求while(printer_status!=READY);//等待直到打印機狀態(tài)為“就緒”*printer_data_port=kernbuf[i];//向數(shù)據(jù)端口輸出第一個字符*printer_control_port=START; //發(fā)送“啟動打印”命令scheduler(); //阻塞用戶進程P,調度其他進程執(zhí)行if(n==0){ //若字符串打印完,則unblock_user(); //用戶進程P解除阻塞,P進就緒隊列}else{*printer_data_port=kernelbuf[i];//向數(shù)據(jù)端口輸出一個字符*printer_control_port=START;//發(fā)送“啟動打印”命令n=n-1; //未打印字符數(shù)減1i=i+1; //下一個打印字符指針加1}acknowledge_interrupt(); //中斷回答(清除中斷請求)return_from_interrupt(); //中斷返回例子:采用中斷方式進行字符串打印sys_write進行字符串打印的程序段:“字符打印”中斷服務程序:sys_write是如何調出來的?系統(tǒng)調用!中斷服務程序是如何調出來的?外設完成任務!輪詢方式和中斷方式的比較舉例:假定某機控制一臺設備輸出一批數(shù)據(jù)。數(shù)據(jù)由主機輸出到接口的數(shù)據(jù)緩沖器OBR,需要1μs。再由OBR輸出到設備,需要1ms。設一條指令的執(zhí)行時間為1μs(包括隱指令)。試分別計算采用輪詢方式和中斷方式的數(shù)據(jù)傳輸速度和對主機的占用率。對主機占用率:在進行I/O操作過程中,處理器有多少時間花費在輸入/出操作上。數(shù)據(jù)傳送速度(吞吐量、I/O帶寬):單位時間內傳送的數(shù)據(jù)量。假定每個數(shù)據(jù)的傳送都要重新啟動!也即,是字符型設備問題:CPU如何把數(shù)據(jù)送到OBR,I/O接口如何把OBR中的數(shù)據(jù)送到設備?CPU執(zhí)行I/O指令來將數(shù)據(jù)送OBR;而I/O接口則是自動把數(shù)據(jù)送到設備。輪詢方式和中斷方式的比較(1)程序直接控制傳送方式若查詢程序有10條,第5條為啟動設備的指令,則:

數(shù)據(jù)傳輸率為:1/(1000+5)μs,約為每秒995個數(shù)據(jù)。主機占用率=100%(2)中斷傳送方式若中斷服務程序有30條,在第20條啟動設備,則:數(shù)據(jù)傳輸率為:1/(1000+1+20)μs,約為每秒979個數(shù)據(jù)。主機占用率為:(1+30)/(1000+1+20)=3%外設CPU5μs1ms輪詢方式為什么中斷服務程序比查詢程序長?因為中斷服務程序有額外開銷,如:保存現(xiàn)場、保存舊屏蔽字、設置新屏蔽字、開中斷、查詢中斷源等外設CPU1ms中斷方式若是磁盤等高速設備與主機交換數(shù)據(jù),那么,采用中斷方式會怎么樣?DMA方式的基本要點DMA方式的基本思想在高速外設和主存間直接傳送數(shù)據(jù)由專門硬件(即:DMA控制器)控制總線進行傳輸DMA方式適用場合高速設備(如:磁盤、光盤等)成批數(shù)據(jù)交換,且數(shù)據(jù)間間隔時間短,一旦啟動,數(shù)據(jù)連續(xù)讀寫采用“請求-響應”方式每當高速設備準備好數(shù)據(jù)就進行一次“DMA請求”,DMA控制器接受到DMA請求后,申請總線使用權DMA控制器的總線使用優(yōu)先級比CPU高,為什么?與中斷控制方式結合使用在DMA控制器控制總線進行數(shù)據(jù)傳送時,CPU執(zhí)行其他程序DMA傳送結束時,要通過“DMA結束中斷”告知CPU讀一個磁盤扇區(qū)-第一步MainmemoryALURegisterfileCPUchipDiskcontrollerGraphicsadapterUSBcontrollermousekeyboardMonitorDiskI/ObusBusinterfaceCPU對DMA控制器初始化:將傳送方向(讀)、傳送數(shù)據(jù)個數(shù)、磁盤邏輯塊號、主存起始地址等參數(shù)送到DMA控制器發(fā)送”啟動DMA傳送“命令DMA控制器傳送數(shù)據(jù)個數(shù)被送到計數(shù)器中讀一個磁盤扇區(qū)–第二步MainmemoryALURegisterfileCPUchipDiskcontrollerGraphicsadapterUSBcontrollerMouseKeyboardMonitorDiskI/ObusBusinterface磁盤控制器讀相應的扇區(qū),并由DMA控制器控制總線把數(shù)據(jù)從磁盤控制器送主存,此時,CPU執(zhí)行其他進程DMA控制器每傳送一個數(shù)據(jù),則計數(shù)器減1讀一個磁盤扇區(qū)–第三步MainmemoryALURegisterfileCPUchipDiskcontrollerGraphicsadapterUSBcontrollerMouseKeyboardMonitorDiskI/ObusBusinterface當DMA傳送結束(計數(shù)為0),DMA控制器向CPU發(fā)出“DMA結束中斷請求”,要求CPU進行相應的后處理。DMA控制器DMA方式下CPU的工作copy_string_to_kernel(strbuf,kernelbuf,n);//將字符串復制到內核緩沖區(qū)initialize_DMA(); //初始化DMA控制器(準備傳送參數(shù))*DMA_control_port=START; //發(fā)送“啟動DMA傳送”命令scheduler(); //阻塞用戶進程P,調度其他進程執(zhí)行acknowledge_interrupt(); //中斷回答(清除中斷請求)unblock_user(); //用戶進程P解除阻塞,進入就緒隊列return_from_interrupt(); //中斷返回例子:采用DMA方式進行字符串輸出sys_write進行字符串輸出的程序段:”DMA結束“中斷服務程序:CPU僅在DMA控制器初始化和處理“DMA結束中斷“時介入,在DMA傳送過程中不參與,因而CPU用于I/O的開銷非常小。DMA控制器接受到“啟動”命令后,控制總線進行DMA傳送。通常用”周期挪用法“:設備每準備好一個數(shù)據(jù),挪用一次”存儲周期“,使用一次總線事務進行數(shù)據(jù)傳送,計數(shù)器減1。計數(shù)器為0時,發(fā)送DMA結束中斷請求例:中斷、DMA方式下CPU的開銷

設處理器按500MHz的速度執(zhí)行,磁盤控制器中有一個16B的數(shù)據(jù)緩存器,磁盤傳輸速率為4MB/Sec,在磁盤傳輸數(shù)據(jù)過程中,要求沒有任何數(shù)據(jù)被錯過,并假定CPU訪存和DMA訪存沒有沖突。(1)若用中斷方式,每次傳送的開銷(包括用于中斷響應和處理的時間)是500個時鐘周期。如果硬盤僅用5%的時間進行傳送,那么處理器用在硬盤I/O操作上所花的時間百分比(主機占用率)為多少?(2)若用DMA方式,處理器用1000個時鐘進行DMA傳送初始化,在DMA完成后的中斷處理需要500個時鐘。如果每次DMA傳送8000B的數(shù)據(jù)塊,那么當硬盤進行傳送的時間占100%(即:硬盤一直進行讀寫,并傳輸數(shù)據(jù))時,處理器用在硬盤I/O操作上的時間百分比(主機占用率)為多少?

中斷傳送:硬盤每次中斷,可以以16字節(jié)為單位進行傳送,為保證沒有任何數(shù)據(jù)被錯過,應達到每秒4MB/16B=250k次中斷的速度;每秒鐘用于中斷的時鐘周期數(shù)為250kx500=125x106;在一次數(shù)據(jù)傳輸中,處理器花費在I/O上的時間的百分比為:

125x106/(500x106)=25%假定硬盤僅用其中5%的時間來傳送數(shù)據(jù),則處理器花費在I/O方面的百分比為25%x5%=1.25%。例:中斷、DMA方式下CPU的開銷

設處理器按500MHz的速度執(zhí)行,硬盤控制器中有一個16B的數(shù)據(jù)緩存器,磁盤傳輸速率為4MB/Sec,在磁盤傳輸數(shù)據(jù)過程中,要求沒有任何數(shù)據(jù)被錯過,并假定CPU訪存和DMA訪存沒有沖突。(1)若用中斷方式,每次傳送的開銷(包括用于中斷響應和處理的時間)是500個時鐘周期。如果硬盤僅用5%的時間進行傳送,那么處理器用在硬盤I/O操作上所花的時間百分比(主機占用率)為多少?(2)若用DMA方式,處理器用1000個時鐘進行DMA傳送初始化,在DMA完成后的中斷處理需要500個時鐘。如果每次DMA傳送8000B的數(shù)據(jù)塊,那么當硬盤進行傳送的時間占100%(即:硬盤一直進行讀寫,并傳輸數(shù)據(jù))時,處理器用在硬盤I/O操作上的時間百分比(主機占用率)為多少?

DMA傳送:每次DMA傳送將花費8000B/(4MB/Sec)≈2x10-3秒;一秒鐘內有1/(2x10-3)=500次DMA傳送;如果硬盤一直在傳送數(shù)據(jù)的話,處理器必須每秒鐘花(1000+500)x500=750x103個時鐘周期來為硬盤I/O操作服務;在硬盤I/O操作上處理器花費的時間占:

750x103/(500x106)=1.5x10-3=0.15%

一秒鐘內有4MB/8000B=500次DMA傳送內核空間I/O軟件所有用戶程序提出的I/O請求,最終都通過系統(tǒng)調用實現(xiàn)通過系統(tǒng)調用封裝函數(shù)中的陷阱指令轉入內核I/O軟件執(zhí)行內核空間I/O軟件實現(xiàn)相應系統(tǒng)調用的服務功能內核空間的I/O軟件分三個層次設備無關軟件層設備驅動程序層中斷服務程序層設備驅動程序層、中斷服務程序層與I/O硬件密切相關系統(tǒng)調用服務例程,被陷阱指令調出執(zhí)行,一旦發(fā)送“啟動”命令,則所代表的進程被送等待隊列(即被阻塞)外設CPU啟動完成啟動完成工作工作工作請求響應啟動請求響應返回P被阻塞QQQP中斷服務程序中斷控制和DMA控制兩種方式下都需進行中斷處理中斷控制方式:中斷服務程序主要進行從數(shù)緩器取數(shù)或寫數(shù)據(jù)到數(shù)緩器,然后啟動外設工作DMA控制方式:中斷服務程序進行數(shù)據(jù)校驗等后處理工作在內核I/O軟件中用到的I/O指令、“開中斷”和“關中斷”等指令都是特權指令,只能在操作系統(tǒng)內核程序中使用

I/O操作的實現(xiàn)分以下三個部分介紹第一講:用戶空間I/O軟件I/O子系統(tǒng)概述文件的基本概念用戶空間的I/O函數(shù)第二講:內核空間I/O軟件與設備無關的I/O軟件設備驅動程序中斷服務程序第三講:I/O硬件和軟件的接口I/O設備和系統(tǒng)互連I/O端口及其編址方式I/O模塊中的中斷系統(tǒng)Intel體系結構中特指的“系統(tǒng)總線”北橋芯片組把處理器–存儲器總線分成了兩個總線:處理器總線(系統(tǒng)總線,前端總線)

存儲器總線

系統(tǒng)總線指系統(tǒng)總線上傳輸?shù)男畔⒂心男繑?shù)據(jù)(指令、操作數(shù)、中斷號)、地址、其他控制/狀態(tài)/定時等信號!I/O總線系統(tǒng)總線通常由一組控制線、一組數(shù)據(jù)線和一組地址線構成。也有些總線沒有單獨的地址線,地址信息通過數(shù)據(jù)線來傳送,這種情況稱為數(shù)據(jù)/地址復用。數(shù)據(jù)線(DataBus):承載在源和目部件之間傳輸?shù)男畔ⅰ?shù)據(jù)線的寬度反映一次能傳送的數(shù)據(jù)的位數(shù)。地址線(AddressBus):給出源數(shù)據(jù)或目的數(shù)據(jù)所在的主存單元或I/O端口的地址。地址線的寬度反映最大的尋址空間。控制線(ControlBus):控制對數(shù)據(jù)線和地址線的訪問和使用。用來傳輸定時信號和命令信息。典型的控制信號包括:時鐘(Clock):用于總線同步復位(Reset):初始化所有設備總線請求(BusRequest):表明發(fā)出該請求信號的設備要使用總線總線允許(BusGrant):表明接收到該允許信號的設備可以使用總線中斷請求(InterruptRequest):表明某個中斷正在請求中斷回答(InterruptAcknowledge):表明某個中斷請求已被接受存儲器讀(memoryread):從指定的主存單元中讀數(shù)據(jù)到數(shù)據(jù)總線上存儲器寫(memoryread):將數(shù)據(jù)總線上的數(shù)據(jù)寫到指定主存單元中I/O讀(I/Oread):從指定的I/O端口中讀數(shù)據(jù)到數(shù)據(jù)總線上I/O寫(I/OWrite):將數(shù)據(jù)總線上的數(shù)據(jù)寫到指定的I/O端口中傳輸確認(transmissionAcknowledge):數(shù)據(jù)已被接收或已送總線系統(tǒng)總線的組成總線的基本概念總線裁決早期:總線多是共享傳輸,需確定哪個設備使用總線。

現(xiàn)在:總線多是點對點傳輸,無需裁決。總線定時定義總線事務中的每一步何時開始、何時結束。

Synchronous(同步):用時鐘信號來確定每個步驟

Asynchronous(異步):用握手信號來定時,前一個信號結束就是下一

個信號的開始

半同步:結合使用時鐘信號和握手信號來定時并行/串行傳輸

并行傳輸:一個方向同時傳輸多位數(shù)據(jù)信號,故位與位需同步,慢!

串行傳輸:一個方向只傳輸一位數(shù)據(jù)信號,無需在位之間同步,快!現(xiàn)在總線設計的趨勢是:點對點、同步、串行總線寬度總線中數(shù)據(jù)線的條數(shù),決定了每次能同時傳輸?shù)男畔⑽粩?shù)總線工作頻率每秒傳送次數(shù)(MT/s或GT/s)。早期

溫馨提示

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

評論

0/150

提交評論