天天看點

Linux 标準I/O程式設計 流緩沖 檔案流

今天學完底層檔案i/o和标準i/o程式設計

底層i/o操作基于檔案描述符。

标準i/o操作基于流緩沖

C語言中流的概念

一句話,流(steam)表示任意輸入源或任意輸出的目的地。很多程式是通過一個或多個流進行讀入和輸出的。這些流可能存儲在不同的媒體(如硬碟,CD,DVD,閃存等等),也可能是不存儲檔案的裝置(列印機,網絡套接字)。頭檔案<stdio.h>中定義了處理流的函數。(注意不僅僅隻有表示檔案的流)

C程式中對流的操作是通過

FILE *

實作的。是以說這種資料類型表示的就是一個流。一個流對象儲存了和檔案(也可能不是檔案,但是Unix下幾乎“一切”都是檔案)連接配接的情況以及緩沖的狀态,還有檔案位置定位符的狀态。每一個流還有一個檔案末位訓示器和錯誤訓示器,可以通過ferror和feof來監測。(參見EOF and Errors.)

注意不要試圖建立自己的

FILE *

, 讓函數庫去實作。

流與檔案描述符差別與聯系

一句話,流是檔案描述符的抽象,一般使用檔案描述符是系統層次的調用。

當向一個檔案讀入或者輸出時,既可以選擇流,也可以選擇使用檔案描述符。檔案描述符是

int

類型的,而流是用

FILE *

來表示的。

檔案描述符提供了一個原始、低層次的輸入輸出接口。檔案描述符和流都可以表示一個連接配接,可以是和裝置的(例如終端),或者管道,或者一個和另一個程序的套接字,或者就是一個正常的檔案(normal file)。但是,如果你想要對特殊裝置進行特定的操作,你必須使用檔案描述符。另外,如果你的程式需要以特殊模式進行輸入輸出(例如nonblocking, polled input, 參見File Status Flags),也必須使用檔案描述符。

而流提供了一個基于原始的檔案描述符的高層次接口。流接口對于所有類型的檔案的操作大多都是類似的,唯一的差別就是緩沖的政策(參見下面的流緩沖)。

使用流的主要優勢是操作流的函數比檔案描述符多得多,而且更加強大友善。檔案描述符僅僅提供了一個單一的函數用來傳輸字元塊,但是流接口提供了很多格式化的輸入輸出(例如printf和scanf)和一些字元函數以及列讀入輸出函數。

因為流是基于檔案描述符的,是以實際上你可以“拆解”一個流得到對應的檔案描述符然後進行低層次的操作。相反地,你也可以先用檔案描述符和一個檔案建立連接配接,然後建立一個連結這個檔案描述符的流對象。

通常情況下,你都應該使用流來進行輸入輸出,這樣不僅友善強大,而且可以保證程式的移植性:你可以在任何一個遵守ISO C标準的機器上使用流,但是在一個非GNU機器上你可能無法使用檔案描述符。

緩沖為什麼存在?

标準I/o庫提供緩沖的目的是盡可能減少使用read和write調用的次數

正如上面談到的,緩沖區可能導緻輸出延遲,那麼它為什麼存在呢?

在系統底層調用這個層次,資料是用write+檔案描述符寫入的,這種方法将資料寫入到檔案描述符對應的一個位元組緩沖中。

大多數語言有着非常快的函數調用,在C/C++這種語言中調用一個函數可能隻需要幾個cpu周期,時間開銷幾乎可以忽略不計(隻有在近端的情況下才會使用inline.)。然而,一個系統調用時間開銷是非常可觀的。在Linux上的一個系統調用可能會花費幾千個cpu周期并摻雜着上下文轉化.是以系統調用比使用者空間裡的函數調用花的時間多得多。

緩沖存在的主要目的就是為使用者空間函數抵消調用系統函數的開銷。當函數做很多寫入操作時這非常重要——否則系統調用的時間會占程式運作時間的主要部分。

讓我們考慮一下當你使用 grep在一個檔案或者stdin中搜尋字段。假設你在nginx日志中找一個IP位址,而在nginx日志裡一行大概有100個字元。如果不使用緩沖的話,就意味着隻要grep遇到了想要的ip位址,它就會調用

write()

.,而這會接連不斷的發生,并且每次寫入的字元大概都在100位元組。如果使用預設緩沖4096位元組的話,grep會等到緩沖區滿以後再調用

write()

清除緩沖,這大概會讀入40行才發生一次(譯者注:緩沖政策是寫入流/檔案的充分條件,不是必要的。緩沖區存在的意義就是使用“Stream-level I/O”時從緩沖區進行異步塊寫入/讀出,這樣可以在裝置堵塞或者大量的寫入操作的時候加快效率。如果一次隻寫入少量資料,核心一看沒有堵塞,“幹脆”就把緩沖區的内容寫入了,反正放着也是放着。滿緩沖在gnu library c 中定義為以任意大小的塊寫入流。),是以會減少大概40倍在系統調用上花的時間。不錯!

如果grep程式向标準輸出寫入大量的資料的話,你可能都不會注意到緩沖區造成的延遲。并且如果grep隻是找一個簡單的字段,通常它在輸出上花的時間會比尋找該字段畫的時間更多。但是假如grep比對字段發生的非常緩慢,例如每十秒鐘才發生一次,那麼我們可能要等400秒才能得到一個輸出!(即便在開始的時候已經比對到幾個字段)

從這邊網頁得到啟發:https://www.cnblogs.com/liqiuhao/p/7676125.html

設計标準i/o程式設計就是基于流緩沖:

用使用者空間函數抵消調用系統函數的開銷,先讀寫到緩沖區,根據你設定的緩沖區滿了,然後寫/讀

繼續閱讀