本文來自個人博客:https://dunkwan.cn
函數readv和writev
readv
和writev
函數用於在一次函數調用中讀、寫多個非連續緩衝區。有時也將這兩個函數稱爲散佈讀和聚集寫。
#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
兩個函數的返回值:已讀或已寫的字節數;若出錯,返回-1。
兩個函數的第二個參數是指向iovec
結構數組的一個指針。
struct iovec{
void *iov_base; /* starting address of buffer */
size_t iov_len; /* size of buffer */
};
writev
函數從緩衝區中聚集輸出數據的順序時:iov[0]
、iov[1]
、直至iov[iovcnt-1]
。writev
返回輸出的字節總數,通常應等於所有緩衝區長度之和。
readv
函數則將讀入的數據按上述同樣的順序散佈到緩衝區中。readv
總是先填滿一個緩衝區,然後在填寫下一個。readv
返回讀到的字節總數。如果遇到文件尾端,已無數據可讀,則返回0。
函數readn
和writen
考慮到管道、FIFO以及某些設備有下列性質:
- 一次
read
的操作可能少於所要求的的數據。 - 一次
write
的操作可能少於指定輸出的字節數。
readn
和writen
函數功能則是分別讀、寫指定的N字節數據,並處理返回值小於要求值的情況。這兩個函數只是按需多次調用read
和write
直到讀、寫了N字節數據。
#include <apue.h>
ssize_t readn(int fd, void *buf, size_t nbytes);
ssize_t writen(int fd, void *buf, size_t nbytes);
兩個函數返回值:讀、寫的字節數;若出錯,返回-1。
上面兩個函數並不是某個標準的組成部分,只是爲了方便定義的。
詳細定義如下:
#include "apue.h"
ssize_t /* Read "n" bytes from a descriptor */
readn(int fd, void *ptr, size_t n)
{
size_t nleft;
ssize_t nread;
nleft = n;
while (nleft > 0) {
if ((nread = read(fd, ptr, nleft)) < 0) {
if (nleft == n)
return(-1); /* error, return -1 */
else
break; /* error, return amount read so far */
} else if (nread == 0) {
break; /* EOF */
}
nleft -= nread;
ptr += nread;
}
return(n - nleft); /* return >= 0 */
}
ssize_t /* Write "n" bytes to a descriptor */
writen(int fd, const void *ptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) < 0) {
if (nleft == n)
return(-1); /* error, return -1 */
else
break; /* error, return amount written so far */
} else if (nwritten == 0) {
break;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n - nleft); /* return >= 0 */
}
存儲映射I/O
存儲映射I/O能將一個磁盤文件映射到存儲空間中的一個緩衝區上,於是,當從緩衝區取數據時,就相當於讀文件的相應字節。與此類似,將數據存入緩衝區時,相應字節就自動寫入文件。
mmap
函數告訴內核將一個給定的文件映射到一個存儲區域中。
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int port, int flag, int fd, off_t off);
返回值:若成功,返回映射區的起始地址;若出錯,返回MAP_FAILED。
參數 | 說明 |
---|---|
addr |
用於指定映射存儲區的起始地址。通常設置爲0,表示由系統選擇該映射區的起始地址。 |
fd |
指定要被映射文件的描述符。在文件映射到地址空間之前,必須打開該文件。 |
len |
被映射的字節數。 |
off |
要映射字節在文件中偏移量。 |
prot |
指定了映射存儲區的保護要求。 |
prot
參數可選值,可以是PROT_NONE
,也可以是PROT_READ
、PROT_WRITE
和PROT_EXEC
的任意組合的按位或。對指定映射存儲區的保護要求不能超過文件open
模式訪問權限。例如,若該文件是隻讀打開的,那麼對映射存儲區就不能指定PROT_WRITE
。
flag
參數可選值如下:
MAP_FIXED
返回值必須等於
addr
。因這不利於可移植性,所以不鼓勵使用此標誌。如果未指定此標誌,而且
addr
非0,則內核只把addr
視爲在何處設置映射區的一種建議,但是不保證會使用所要求的地址。將addr
指定爲0可獲得的最大可移植性。
MAP_SHARED
這一標誌描述了本進程對映射區所進行的存儲操作的配置。此標誌指定存儲操作修改映射文件,也就是,存儲操作相當於對該文件的
write
。必須指定本標誌或下一個標誌(MAP_PRIVATE
),但不能同時指定兩者。
MAP_PRIVATE
本標誌說明,對映射區的存儲操作導致創建該映射文件的一個私有副本。所有後來對該映射區的引用都是引用該副本。(此標誌的一種用途是用於調試程序,它將程序文件的正文部分映射至存儲區,但允許用戶修改其中的指令。任何修改隻影響程序文件的副本,而不影響原文件。)
下圖是存儲映射文件。
mprotect
函數可以更改一個現有映射的權限。
#include <sys/mman.h>
int mprotect(void *addr, size_t len, int prot);
返回值:若成功,返回0;若出錯,返回-1。
如果共享映射中的頁已修改,那麼可以調用msync
將該頁沖洗到被映射的文件中。
#include <sys/mman.h>
int msync(void *addr, size_t len, int flags);
返回值:若成功,返回0;若出錯,返回-1。
flags
參數
MS_ASYNC
簡單調用要寫的頁。
MS_SYNC
在返回前等待寫操作完成。
MS_INVALIDATE
用於通知操作系統丟棄那些與底層存儲器沒有同步的頁。
當進程終止時,會自動解除存儲區的映射,或者直接調用munmap
函數也可以解除映射區。關閉映射存儲區時使用的文件描述符並不解除映射區。
#include <sys/mman.h>
int munmap(void *addr, size_t len);
返回值:若成功,返回0;若出錯,返回-1。
測試示例:
用存儲映射I/O實現複製文件(類似cp命令)。
#include "apue.h"
#include <fcntl.h>
#include <sys/mman.h>
#define COPYINCR (1024*1024*1024) /* 1 GB */
int
main(int argc, char *argv[])
{
int fdin, fdout;
void *src, *dst;
size_t copysz;
struct stat sbuf;
off_t fsz = 0;
if (argc != 3)
err_quit("usage: %s <fromfile> <tofile>", argv[0]);
if ((fdin = open(argv[1], O_RDONLY)) < 0)
err_sys("can't open %s for reading", argv[1]);
if ((fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC,
FILE_MODE)) < 0)
err_sys("can't creat %s for writing", argv[2]);
if (fstat(fdin, &sbuf) < 0) /* need size of input file */
err_sys("fstat error");
if (ftruncate(fdout, sbuf.st_size) < 0) /* set output file size */
err_sys("ftruncate error");
while (fsz < sbuf.st_size) {
if ((sbuf.st_size - fsz) > COPYINCR)
copysz = COPYINCR;
else
copysz = sbuf.st_size - fsz;
if ((src = mmap(0, copysz, PROT_READ, MAP_SHARED,
fdin, fsz)) == MAP_FAILED)
err_sys("mmap error for input");
if ((dst = mmap(0, copysz, PROT_READ | PROT_WRITE,
MAP_SHARED, fdout, fsz)) == MAP_FAILED)
err_sys("mmap error for output");
memcpy(dst, src, copysz); /* does the file copy */
munmap(src, copysz);
munmap(dst, copysz);
fsz += copysz;
}
exit(0);
}
結果如下:
源代碼