筆者本打算撰寫一篇講解標準I/O(緩存I/O)的博文,可是發現已經有網友作過一樣的工做,而且工做質量上乘,特轉載於此。nginx
原文地址http://lenky.info/archives/2012/08/1856 緩存
利用系統調用函數read()/write()是咱們日常用得最多的一種數據讀寫方式,大多數狀況下咱們並無考慮這種數據讀寫方式的執行效率,由於在不少並不以數據頻繁讀寫爲性能瓶頸的應用程序中函數read()/write()消耗的執行時間能夠忽略,可是它們內在具體實現和執行效率到底如何呢?下面咱們就來進行詳細的分析。
函數read()/write()定義在頭文件unistd.h內,原型以下:服務器
#include <unistd.h> ssize_t read(int fildes, void *buf, size_t nbyte); ssize_t write(int fildes, const void *buf, size_t nbyte);
在這裏我並不打算講解函數read()/write()的源碼,簡略的描述其執行過程,涉及到的主要調用關係以下圖所示:函數
若是咱們的服務器程序,好比nginx採用read()/write()數據讀寫傳輸方式,當某客戶端發送「GET /index.htm HTTP/1.1」請求時,nginx則需將存放在站點根目錄的index.htm文本文件看成響應數據發送給客戶端。當沒有啓用mmap()的狀況下,nginx完成這個響應數據的發送工做須要兩步,首先利用函數read()將index.htm文本文件數據讀入內存,接着利用函數write()將第一步讀入內存的數據寫到鏈接套接口描述符來完成響應數據的發送:性能
如上圖所示,nginx應用程序利用read()/write()數據讀寫傳輸方式完成響應數據的發送工做一共須要4次上下文切換和4次數據拷貝(即假定爲一次read()/write()就將index.htm文本文件數據發送完畢的狀況,若是不僅一次則切換和拷貝次數將會更多),這些切換和拷貝過程是否是必須的呢?答案是否認的。好比當在啓用mmap()的狀況下就能夠減小一次數據拷貝,此時利用系統調用mmap()將文本文件index.htm數據拷貝到內核緩存區,並將拷貝映射目標地址的起始值返回給nginx應用程序,正是由於nginx應用程序有了這塊內核緩存區的映射起始地址而且能夠共享這塊內核緩存區(系統調用mmap()實現的結果),所以在第一幅圖中,從內核Buffer到用戶Buffer再到Socket Buffer的拷貝就能夠變成一次從內核Buffer到Socket Buffer的直接拷貝:spa