高級文件I/O
系列文章目錄:/content/24588526.html
前言
第二章介紹了稱為文件I/O的一系列系統調用,這些系統調用是文件操作的基礎。這章介紹Linux提供的更高級的文件I/O方式,包括新的I/O方式、更高級的I/O多路復用方式以及內存映射等等。通過本章的學習,可以更加靈活高效的使用系統調用進行文件操作。
4.1 分散-聚合I/O
分散-聚合I/O(scatter-gather I/O)還有很多別的叫法,例如分散輸入、集中輸出,向量I/O、散布/聚合I/O等等,這裡指的是同一個I/O方式,都是從一個文件描述符中讀取數據並拷貝到不同的用戶空間緩沖區或從不同的用戶空間緩沖區將數據寫入到同一個文件描述符。與此相對的,前面章節介紹的I/O方式可以稱作線性I/O。
Linux提供了專門的系統調用(意味著是原子操作)來支持分散-聚合I/O,是對應文件I/O後面加上’v’(代表vector,向量)。
writev()
[code]#include <sys/uio.h>
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
struct iovec類型標明了緩沖區的地址和長度:
[code]//定義在<sys/uio.h>
struct iovec {
void *iov_base; /* 緩沖區地址 */
size_t iov_len; /* 能操作的字節數 */
};
iovcnt-有多少個iovec結構需要操作,其最大值為定義在
[code]#include <sys/uio.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
char buf1[]="Failure is the mother of success.";
char buf2[]="——Thomas Paine\n";
struct iovec iovArr[2];
iovArr[0].iov_base=buf1;
iovArr[0].iov_len=strlen((char*)iovArr[0].iov_base);
iovArr[1].iov_base=buf2;
iovArr[1].iov_len=strlen((char*)iovArr[1].iov_base);
printf("writev():%ld bytes,buf1:%d,buf2:%d\n", writev(STDOUT_FILENO, iovArr, 2),strlen((char*)iovArr[0].iov_base),strlen((char*)iovArr[1].iov_base));
/*Failure is the mother of success.——Thomas Paine
writev():50 bytes,buf1:33,buf2:17*/
return 0;
}
readv()
[code]#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
其參數、返回值與writev()類似。讀取時是按照iovec結構的順序依次讀取,只有上一個iovec結構讀取到iov_len個數據之後,輸入才會寫入下一個結構,因此可能會出現有些結構沒有值的情況。感興趣的同學可以將上面writev()的測試函數修改一下,改為聚合輸出到文件,再用readv()讀取並顯示,此處不再寫代碼了。
分散-聚合I/O的優勢
其最大的優勢就是能夠在一個系統調用內操作不同的緩沖區,如果用傳統的read()方式讀取一整塊數據,分別分配給不同的緩沖區,不光帶來效率上的問題,還增加了代碼的復雜度。
其次是單個系統調用保證了原子性,在多線程環境下不需要考慮線程同步(當然,如果多個線程對用戶空間的iovec結構做讀寫,還是要靠線程同步機制來保證可靠性的)。
還有一些不那麼普遍的好處,假設我們的程序通過某種依賴長度的協議做進程間通信,例如前8個字節保存某類信息,緊跟著的32個字節保存另一類信息。使用分散-聚合I/O可以一次性讀出數據並按照長度分配到對應的緩沖區中。
pwritev()和preadv()
Linux還提供了與pwrite()/pread()類似的支持定位讀寫的系統調用(想了解pwrite()和pread()可以參考前面章節筆記):
[code]#include <sys/uio.h>
ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset);
ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset);
這裡再次強調一下,pwrite()系列的定位讀寫適合和多線程結合起來,因為定位讀寫系列函數並不改變進程中保存的文件讀寫位置,也就不存在多線程同步的問題了。