轉載:http://passer-byb.com/blog/85.html
這個問題折騰了我有一個星期了,因為最近一直在搞有關磁碟的I/O的測試,因此需要繞過或者關閉VFS的cache直接對磁碟進行讀寫。Google過之後發現基本上想要關閉cache是不可能的,只有想辦法繞過了。於是很自然的想到了write()和read()這兩個東西,可是當時並沒意識到buffer跟cache並不是一個東西。write()跟read()雖然叫直接I/O但事實上他們只是繞過了buffer,也就是說在讀入跟輸出的時候不進行任何的緩衝,但VFS本身的cache仍然存在,VFS仍然會選擇在合適的時候將cache的資料寫入磁碟。
於是找了很多方法,有的是通過drop cache的方法,就是"echo 1 > /proc/sys/vm/drop_caches"這樣的方法,不過這種方法只能清除一次cache,讀寫一次,很是麻煩。於是果斷放棄了這種不靠譜的方法。
後來想到了APUE上沒有提到的一個O_DIRECT flag(因為APUE講的是UNIX編程,當然沒必要照顧到GNU Linux特有的東西)。具體怎麼用呢,下面就來說說。
首先在檔案頭需要定義
#define _GNU_SOURCE
然後,這個東西用在open()獲得檔案描述符時所填寫的第二個參數,也就是open(path,flag,mode)的第二個參數。比如你要以寫方式建立一個路徑為"/home/user/test.test"新檔案,並且要直接I/O繞過cache,就可以這樣寫:
int fd=open("/home/user/test.test",O_WRONLY|O_CREAT|O_DIRECT,S_IRUSR|S_IWUSR);
之後在你需要read()或者write()的時候,傳入的buffer的首地址,以及buffer的大小都必須跟檔案系統的page size對齊。這個page size可以通過getpagesize()獲得。所以這裡要用到一個我的笨辦法:
int buffersize = getpagesize()*page_num;
int pagesize=getpagesize();
char * t_buf = (char *) malloc(buffersize+pagesize);
char * buffer = (char *)(((unsigned int)buffersize+pagesize-1)/pagesize*pagesize);
之後你就可以用這個buffer來read()或者write()了。如:
read(fd,buffer,buffersize);
要注意的是這裡的buffersize必須是頁大小的整數倍,並且buffer指向的地址也必須是頁大小的整數倍。用上面的簡單計算就可以得到滿足條件的buffer。之後的I/O便是完全繞過cache的direct I/O了。