C語言的輸入輸出模型

2021-07-24 15:26:27 字數 2582 閱讀 3000

參考:

其實對於計算機器的理解,難點是為什麼會有這個模型的建立,也就是模型建立的實際意義,另外還有一點,這個模型的形象化表示,如果能理解這個模型的形象化表示,就可以更深刻的理解模型。

網路或者書籍往往不能這樣去解釋模型的意義以及模型的形象化表示,所以學習計算機的初期,你或許會有點進步,但是學到最後往往會很迷茫,這時候有個導師幫助你顯得尤為重要。

個人以前並沒有深入去研究輸入輸出模型,但是,網路程式設計卻使用了這個模型,導致在做資料傳輸的時候各種迷茫,只能退到c語言的層面,了解c語言的基本輸入輸出模型,因為soket描述符其實就是乙個檔案描述符,但是這個描述符有其特殊的意義,但是,這個描述符的輸入輸出模型和一般性的檔案描述符都是類似的。

先給出模型圖,稍後再解釋這個模型圖:

首先,大部分的輸入輸出模型就是上圖所展示的,部分輸入輸出模型是上圖的簡化,其餘的我暫時沒有概念。

我們先來描述這個模型圖,然後解釋每個組成部分的意義(這些都是個人的見解,不一定對,但是絕對有參考價值)。

「裝置」其實就是檔案描述符所指定的東西,這個「裝置」對應linux作業系統就是檔案(一切皆檔案),對應實際的物理裝置就是硬碟、網絡卡、鍵盤、顯示器等等。我們為了理解上的方便,暫時定義這個「裝置」就是「檔案」。

「輸出緩衝區」與「輸入緩衝區」這部分對應作業系統來說一般就是「記憶體」這個物理裝置,但是,我們不關心他的具體形式,我們關心的是,「**」部分可以直接對這部區域進行讀或寫,那麼問題來了,「**」部分能不能直接對「裝置」讀或寫呢?答案是肯定的。既然可以直接讀寫「裝置」,為什麼還要有「輸入輸出緩衝區」呢?這個我後文進行闡述,這也是整個模型理解起來最困難的地方。

「**」部分就是我們c語言編輯的部分,這部分對於程式設計師來說是可見的部分。

「a」過程是把「裝置」的內容寫入到「輸入緩衝區」,對應的作業系統api是「read」(低階i/o)。

「b」過程是把「輸入緩衝區」的內容寫入「**」,對應的標準庫api是「getc」等(高階i/o)

「c」過程是把「**」的內容寫入到「輸出緩衝區 」,對應的標準庫api是「putc」等(高階i/o)

「d」過程是把「輸出緩衝區」寫入到「裝置」, 對應的作業系統api是「write」(低階i/o)

對於實際的實現,「輸入輸出緩衝區」部分還有一些特性,我們忽略這個特性,我們簡單的模擬一下,**是如何實現讀的操作的,我們以標準輸入stdin做為例子。

我們在終端部分輸入乙個內容,當我們按下回車的時候,作業系統喚醒**的讀取**(比如我們正在等待getc),假設我們的輸入時「abcd回車」,然後這個內容就首先會寫入到輸入緩衝區(a過程),假設輸入緩衝區足夠的大,那麼「abcd回車」就會全部寫入到「輸入緩衝區」,接著就會寫入到**(b過程),這時候我們的**緊緊消費了乙個字元「a」,輸入緩衝區還有「bcd回車」等,如果我們再次呼叫getc,我們的緊緊會執行b過程,這時候的getc也不會阻塞,因為輸入緩衝區是有資料的而且是足夠的,如果我們持續消費,導致輸入緩衝區為空,我們再次使用getc函式,這時候getc函式就會阻塞,等待喚醒,整個過程就會重複。(個人沒有**實踐,可以的話最好使用**實驗一下)

stdout擁有類似的過程,但是這還是有些不一樣,c過程的部分就是putc所做的工作,d過程是write控制的,這部分一般會有兩個問題,d過程是否可能會阻塞?答案是肯定的,d過程可能會阻塞的,比如網絡卡裝置的寫,如果使用tcp協議,tcp協議是有流量控制的,這時候d過程就會阻塞。d過程什麼時候執行?一般有兩種方式,一種是自動的,比如設定乙個條件,當「輸出緩衝區」滿的時候,執行d過程,還有一種方式是手動的,比如我們可以執行fflush()方法,或者執行fclose()方法,這些方法就會執行d過程。

如果能夠理解上面所說的流程方式,我在說下,我對「輸入輸出緩衝區」的理解,這部分有幾個好處:

我們可以遮蔽掉低階i/o的實現,低階i/o的實現依賴作業系統本身核心的實現,所以如果能夠遮蔽這部分的差異,我們可以很容易寫出可移植的程式。

我們可以使用這部分的內容實現「行」讀取的行為,對於計算機而言是沒有「行」這個概念,有了這部分,我們可以定義「行」的概念,然後解析緩衝區的內容,返回乙個「行」。

這裡有個設計性的問題,如果我們想遮蔽裝置的差異性,而裝置之間又有某些相同的特性(比如都是關聯式資料庫),那麼我們就可以利用上面的模型,建立乙個「緩衝區」層,這樣**部分只針對「緩衝區」層進行設計。另外,我們還會發現,當我們使用getc的時候,我們還是需要乙個檔案描述符號,而檔案描述符是針對裝置的,也就是,不論我們怎麼設計**,上層的**必須告訴底層我需要從什麼裝置讀或者寫操作,我們可以使用類似「檔案描述符」的概念去執行這個通知的操作,但是,你還是必須告訴底層,你到底想幹什麼才行。「緩衝區」層只是遮蔽具體的讀或寫的具體方式,但是沒有遮蔽掉裝置本身的差異性。

這部分的內容對於《unix網路程式設計》很有參考意義。因為對於網絡卡的讀或者寫,我們是沒有「緩衝區」層的,標準庫並沒有支援這部分內容,所以我們只能依賴a和d過程。另外,網路程式設計的i/o輸入裝置往往不是單一的,可能是標準輸入裝置和網絡卡裝置同時操作(互動性的程式),這時候,我們往往不能使用標準庫的函式,因為「緩衝區」層對於不是核心的任務,如果有這一層可能會增加**的複雜度。簡單的說,如果你想寫乙個「讀行」這個行為,其實是很困難的,因為你不能把使用緩衝層,就只能從裝置依次讀取單個字元,而這個過程是低效的,從裝置讀我們往往使用塊的方式。

C語言輸入輸出

i o函式 printf 和scanf 輸出程式 函式的呼叫是在c語言標頭檔案 stdio.h 中包含這些像printf 輸入輸出 printf 一般形式如下 printf 格式化字串 輸出參數列 鍵盤輸入函式 顯示器輸出函式printf putchar puts 磁碟檔案操作creatnew 建立...

C語言 輸入 輸出

當我們提到輸入時,這意味著要向程式填充一些資料。輸入可以是以檔案的形式或從命令列中進行。c 語言提供了一系列內建的函式來讀取給定的輸入,並根據需要填充到程式中。當我們提到輸出時,這意味著要在螢幕上 印表機上或任意檔案中顯示一些資料。c 語言提供了一系列內建的函式來輸出資料到計算機螢幕上和儲存資料到文...

C語言 輸入輸出

getchar 函式只能接受單個字元 scanf 格式控制字串 位址表列 輸入時要用乙個以上的空格或回車鍵最為每兩個輸入數之間的間隔 格式字串形式 輸入資料寬度 長度 型別格式 字元意義 d輸入十進位制整數 o輸入八進位制整數 x輸入十六進製制整數 u輸入無符號十進位制整數 f 或 e 輸入實型數 ...