什麼是Direct IO
Direct IO 其實是 Linux 作業系統中的一個概念。它的意思是直接操作文件流,為什麼說是直接呢?其實在我們的作業系統進行檔案操作的時候,並不是馬上直接就在磁碟上進行檔案的讀寫,中間還有一層頁快取。既然是緩存,那麼它當然會帶來一定的效能提升,但這並不是完全絕對的。而直接操作就是忽略掉這一層的快取操作,直接對磁碟上的檔案進行讀寫。我們都知道,磁碟,即使是固態硬碟,它和 CPU 以及記憶體的處理速度之間都是有著巨大的落差的,預設的頁快取就是用來彌補這種差距。但是頁緩存會加大 CPU 的運算操作以及佔用內存,而直接操作則不會有這種問題,但是相對來說,它的速度並不能和帶緩存的文件讀取操作相媲美。
以上是關於 Direct IO 的一個簡單的理解,更詳盡的解釋大家可以參考文末參考文檔中第二條連結的內容並進行深入的學習。在 PHP 中,我們直接在 PECL 下載 Direct IO 擴充功能就可以按照擴充的正常安裝方式進行安裝使用。
建立寫入檔案
既然是檔案操作,那麼我們首先還是來建立和寫入一些檔案資料。
$fd = dio_open("./test", O_RDWR | O_CREAT); echo dio_write($fd, "This is Test.I'm ZyBlog.Show me the money4i"), PHP_EOL; // 43 print_r(dio_stat($fd)); // Array // ( // [device] => 64768 // [inode] => 652548 // [mode] => 35432 // [nlink] => 1 // [uid] => 0 // [gid] => 0 // [device_type] => 0 // [size] => 43 // [block_size] => 4096 // [blocks] => 8 // [atime] => 1602643459 // [mtime] => 1602656963 // [ctime] => 1602656963 // ) dio_close($fd);
和f 系列的函數類似,我們需要使用一個dio_open() 函數來開啟一個文件,O_RDWR | O_CREAT 參數的意思是打開一個可讀寫文件,並且如果文件不存在的話,創建它。這兩個常數是與 Linux 中相關的直接操作檔案的常數對應的,在文末的連結中也可以看到關於這些常數的解釋。
寫入操作也是同樣的使用一個 dio_write() 就能夠完成,它回傳的內容是寫入的內容長度,這裡我們寫入了 43 個字元。
dio_stat() 是傳回目前檔案句柄的一些訊息,我們可以看到裝置號device 、uid 、 gid 、 atime 、 mtime 等一些訊息,它們和我們在Linux 中能夠看到的資訊類似,其實就是這個文件的一些簡單的資訊。
讀取檔案
讀取檔案使用非常簡單的使用一個函數就可以完成。
$fd = dio_open("./test", O_RDWR | O_CREAT); echo dio_read($fd), PHP_EOL; // This is Test.I'm ZyBlog.Show me the money4i dio_close($fd);
dio_read() 函數還包含另一個參數,可以按指定的位元組長度讀取內容,這個在後面我們也會看到相關的範例。
檔案操作
在檔案的讀取過程中,我們有可能只需要讀取一部分的內容,或是從某一位置開始讀取檔案內容,而下面的操作函數就是針對這兩個方面進行操作的。
$fd = dio_open("./test", O_RDWR | O_CREAT); var_dump(dio_truncate ($fd , 20)); // bool(true) echo dio_read($fd), PHP_EOL; // This is Test.I'm ZyB dio_seek($fd, 3); echo dio_read($fd), PHP_EOL; // s is Test.I'm ZyB dio_close($fd);
其實從名稱就可以看出 dio_truncate() 就是用來截斷檔案內容的。在這裡我們從第 20 個字元進行截斷,然後再使用 dio_read() 讀取的內容就只是前 20 個字元的內容了。
dio_seek() 則是指定從哪一個字元開始讀取內容,我們指定開始字元位置為 3 之後,前面三個字元就不會被讀取到了。要注意的是,dio_truncate() 會修改原始檔案的內容,而 dio_seek() 則不會修改。
其它設定
$fd = dio_open('./test', O_RDWR | O_NOCTTY | O_NONBLOCK); dio_fcntl($fd, F_SETFL, O_SYNC); dio_tcsetattr($fd, array( 'baud' => 9600, 'bits' => 8, 'stop' => 1, 'parity' => 0 )); while (($data = dio_read($fd, 4))!=false) { echo $data, PHP_EOL; } // This // is // Test // .I'm // ZyB dio_close($fd);
dio_fcntl() 函數是呼叫的c 函數庫中的fcntl 函數,目的是對檔案描述子執行指定的一些操作,這個操作也是以一些常數固定的,這裡我們使用的是F_SETFL ,它的意思是將檔案描述符標誌設定為指定的值,這個O_SYNC 表示的是如果設定了這個描述符,則對該檔案的寫操盤會等到資料被寫到磁碟上才結束。當然,這個函數還可以設定很多別的操作符,大家可以參考 PHP 的官方文件進行深入的學習。
dio_tcsetattr() 用於設定開啟檔案的終端屬性和波特率。 baud 表示的就是波特率,bits 表示的是位,stop 表示的是停止位,parity 表示的是奇偶校驗位。關於這方面的內容需要 《電腦組成原理》 及 《作業系統》 中的一些知識,我也並不十分地清楚,所以也就不詳細的解釋了。從這裡就可以看出,大學課堂上的那些基礎課程真的非常重要,相信好好學過這些專業基礎課程的同學一定能馬上明白這個函數的作用。
最後,我們在dio_read() 中使用了第二個參數來根據位元組長度讀取檔案內容,可以看到讀取出來的內容是一段一段的以4 個字元長度為單位的輸出。
总结
函数的学习还是比较简单的,核心的还是要知道这个扩展在什么业务场景下更适合使用。在文章开头的介绍中我们已经说明了直接操作文件与普通文件操作的一些区别,在自缓存应用或者需要传输非常大的数据时,直接操作对于 CPU 和 内存 更加地友好。而其它情况,我们还是使用系统默认的文件操作方式就可以了。其实在大部分情况下,我们基本看不出来它们的显著区别。所以在实际应用中,还是那句话,结合业务实际情况,选择最佳的方案。
测试代码: https://github.com/zhangyue0503/dev-blog/blob/master/php/202010/source/4.PHP中DirectIO直操作文件扩展的使用.php 参考文档: https://www.php.net/manual/zh/book.dio.php https://www.ibm.com/developerworks/cn/linux/l-cn-directio/
推荐学习:《PHP视频教程》