從 read 看系統呼叫的耗時

2021-06-06 11:16:59 字數 2745 閱讀 1989

**

1、fread和read有何不同?

先看兩段**:

兩個檔案的功能完全一樣,開啟同乙個名為test.file的檔案,並逐字節地讀取整個檔案。

將它們編譯後得到的可執行程式fread和read分別在同一臺pc(linux系統)上執行,得到的如果如下:

發現沒有?fread與read的耗時相差數十倍之多!可見啊~ read乙個位元組這種寫法是相當不可取的!

2、是什麼引起的差異?

但是,事情為什麼會是這樣的呢?讓我們用strace來看看:

看到了吧~fread庫函式在內部做了快取,每次讀取4096個位元組;而read就老老實實乙個位元組乙個位元組地讀……

那麼再想想,我們讀的是什麼?是磁碟。難道上面提到的差異,就是因為這4096倍的讀磁碟次數差而引起的嗎?並不是這樣。

磁碟是塊裝置,每次讀取的最小單位是塊。而當我們通過系統呼叫讀乙個位元組時,linux會怎麼做呢?它會是讀取乙個塊、然後返回乙個位元組、再把其餘位元組都丟掉嗎?當然不會,這樣的作業系統也太拙劣了……

實際上linux的檔案系統層(fs層)不僅會將每次讀的一整塊資料快取下來,還有預讀機制(一次預讀多個塊,以減少磁碟尋道時間),並且快取的內容是放在檔案對應的inode裡面,是可以在程序間共享的。(省略細節若干……)

那麼,fread與read執行的耗時差別來自於**呢?從**看,它們都做了相同次數的函式呼叫;從核心看,它們都造成了基本上相同的磁碟io……但是注意到,第一段**中一共進行了n(n=約24m)次fread函式呼叫,產生約n/4096次系統呼叫;第二段**中一共進行了n次read函式呼叫,產生n次系統呼叫。實際上這裡的耗時差就來自於4096倍的系統呼叫次數差!fread()庫函式中快取的作用並不是減少讀磁碟的次數,而是減少系統呼叫的次數。

由此可見,系統呼叫比起普通函式呼叫有很大的開銷,編寫**時應當注意避免濫用系統呼叫。

3、進一步提高效率?

為了進一步減少系統呼叫的次數,關於讀檔案的這個問題,我們還可以這樣做:

同樣是遍歷整個檔案,但是讀檔案的過程中不需要使用系統呼叫,直接把檔案當成記憶體buffer來讀就行了。其原理是:mmap的執行,僅僅是在核心中建立了檔案與虛擬記憶體空間的對映關係。使用者訪問這些虛擬記憶體空間時,頁表裡面並沒有這些空間的表項,於是cpu產生缺頁異常。核心捕捉這些異常,逐漸將檔案讀入記憶體,並建立相關的頁表項。(省略細節若干……)

將其編譯後得到的可執行程式mmap和之前的fread、read分別在同一臺pc上執行,得到的如果如下:

mmap方式與fread方式相比,耗時又減少了好幾倍。

4、為什麼?

看到這裡,我們不禁要問,系統呼叫為什麼就這麼耗時呢?系統呼叫與普通函式呼叫到底有什麼不同?

1、兩者都是在呼叫處進行跳轉,轉到被呼叫的**中去執行;

系統呼叫使用的"跳轉"指令相對複雜。因為跳轉到核心空間去執行時,cpu特權級別需要改變(否則沒有許可權訪問到核心空間)。於是,cpu必須封裝一條指令,既實現跳轉、又實現特權級別的改變,並且還要保證跳轉到的地方就是核心**(否則使用者程式用這個指令假跳一下,自己就擁有特權了)。而軟中斷指令恰好能滿足這三點要求,所以,x86下實現系統呼叫的經典方法就是"int 0x80"(現在好像換sysenter了吧~ 但是指令要做的事情應該不會變);

2、兩者都是執行到返回點,然後跳轉回到原先的呼叫點;

系統呼叫的返回過程還伴隨著很多的工作,比如檢查是否需要排程、是否有非同步訊號需要處理、等等。然後,既然來的時候改變了cpu特權級別,返回的時候還得改回去;

3、兩種呼叫中,呼叫前後的**都在相同的虛擬位址空間中(核心空間也屬於使用者程序所能看到的虛擬位址空間範圍內,儘管程序一般情況下沒有許可權去訪問),位址空間並沒有切換;

執行核心**時使用的棧是核心棧,系統呼叫時需要進行棧的切換;

4、兩者的引數傳遞看似相同;

普通函式呼叫是通過棧來傳遞引數的;而系統呼叫是通過暫存器來傳遞引數,暫存器不夠用時才逼不得已使用棧。因為棧要切換,引數傳遞起來不那麼簡單;(但是在這一點上,系統呼叫與普通函式呼叫的耗時並無太大差異。)

5、cpu執行核心**和執行使用者程式**沒什麼區別;

但是注意到,核心**對使用者引數是充分的不信任。以read/fread的buffer引數為例,fread庫函式一般不會檢查buffer引數是否合法。就算想要檢查,也沒這個能力。他不知道buffer是不是個野指標,不知道buffer的大小是否與len不符,不知道buffer指向的這塊記憶體是否可寫……他唯一能做的檢查只是buffer是否為null,可惜這沒什麼意義。但是通過系統呼叫進入核心以後,情況就不同了。前面說到的那些檢查,統統都要做,並且每次呼叫都要不厭其煩地做;

以上幾點區別,僅是我目前能夠想到的。但是管中窺豹,可見一斑。進入核心以後,要做的事情的確是很多很多。

從 read 看系統呼叫的耗時

1 fread和read有何不同?先看兩段 將它們編譯後得到的可執行程式fread和read分別在同一臺pc linux系統 上執行,得到的如果如下 2 是什麼引起的差異?但是,事情為什麼會是這樣的呢?讓我們用strace來看看 那麼再想想,我們讀的是什麼?是磁碟。難道上面提到的差異,就是因為這40...

read系統呼叫

read系統呼叫是glibc庫裡面的乙個函式,是對系統呼叫函式sys read 的封裝與實現。glic庫會將read函式在使用者態下進行解析,通過暫存器將引數儲存起來,並借助於系統呼叫名稱獲得系統呼叫號,該系統呼叫號又可以作為系統呼叫函式在sys call table中的索引獲取函式入口位址,該錶位...

read系統呼叫,mmap系統呼叫

read系統呼叫,mmap系統呼叫 2012 07 23 09 54 28 分類 linux 標籤 linux 檔案系統 虛擬記憶體 儲存系統 字型大小 訂閱 一般情況下,操作檔案既可以使用標準i o,也可直接使用系統呼叫。兩者有何區別呢?在輸入輸出中,直接使用底層的系統呼叫效率是非常低的,為什麼?...