[TOC]
Linux 高級(jí)IO
涉及到一些IO的高級(jí)用法
文件描述符重定向
dup 函數(shù)從當(dāng)前可用的文件描述符中找一個(gè)最小的返回
dup2 用filedes2指定新文件描述符的值。
#include <unistd.h>
int dup(int filedes);
int dup2(int fileds, int filedes2);
/* 成功則返回新的文件描述符,出錯(cuò)返回-1 */
文件描述符之間傳遞數(shù)據(jù)
sendfile,splice,tee
#include <sys.sendfile.h>
int sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
/* 成功時(shí)返回寫到out_fd的數(shù)據(jù)的字節(jié)數(shù),出錯(cuò)時(shí)返回-1并設(shè)置errno */
參數(shù)如下:
- out_fd 數(shù)據(jù)將要寫入的那個(gè)文件描述符
- in_fd 數(shù)據(jù)從這個(gè)描述符取出豌熄,必須是一個(gè)真實(shí)存在的文件涤浇,或者是能夠mmap的設(shè)備
- offset 從文件的哪里開(kāi)始傳輸
- count 要傳輸?shù)淖止?jié)數(shù)
對(duì)于像web服務(wù)器這樣的應(yīng)用墩莫,經(jīng)常需要把某個(gè)文件的內(nèi)容傳輸?shù)娇蛻舳私蚧簿褪菍懙脚c客 戶端通信的socket上蠢护,基本的操作類似于這樣:
open source (disk file)
open destination (network connection)
while there is data to be transferred:
read data from source to a buffer
write data from buffer to destination
close source and destination
數(shù)據(jù)的讀取和寫入需要調(diào)用read和write系統(tǒng)調(diào)用。 在一個(gè)read系統(tǒng)調(diào)用中箍土,數(shù)據(jù)的傳輸主要經(jīng)過(guò)了如下幾個(gè)路徑:
從硬盤中取出數(shù)據(jù) --傳輸?shù)?-> 內(nèi)核緩沖區(qū) --復(fù)制到--> 程序的緩沖區(qū)
而在write系統(tǒng)調(diào)用中中逢享,數(shù)據(jù)傳輸?shù)穆窂絼t是:
程序的緩沖區(qū) --復(fù)制到--> 內(nèi)核緩沖區(qū) --傳輸?shù)?-> 文件或設(shè)備(比如網(wǎng)卡)
進(jìn)程每次使用系統(tǒng)調(diào)用,都會(huì)出現(xiàn)一次在用戶態(tài)和內(nèi)核態(tài)的上下文切換涮帘,大量的系統(tǒng)調(diào)用 消耗的資源是非称床裕可觀的。為了處理這種情況调缨, sendfile 出現(xiàn)了疮鲫,使用 sendfile 時(shí), 數(shù)據(jù)傳輸?shù)穆窂绞牵?/p>
從硬盤中取出數(shù)據(jù) --傳輸?shù)?-> 內(nèi)核緩沖區(qū) --傳輸?shù)?-> 文件或設(shè)備(比如網(wǎng)卡)
省去了數(shù)據(jù)在內(nèi)核空間和用戶空間的兩次傳輸弦叶,稱為零拷貝
進(jìn)程間傳遞文件描述符
socketpair,使用得Unix原始套接字
由于俊犯,父進(jìn)程中打開(kāi)的文件描述符,在fork調(diào)用之后伤哺,子進(jìn)程中仍然打開(kāi)燕侠,并共享同一個(gè)文件表項(xiàng)。所以立莉,文件描述符可以很方便地從父進(jìn)程傳遞到子進(jìn)程绢彤。
需要注意:傳遞一個(gè)文件描述符并不是傳遞文件描述符的值,而是要在教授進(jìn)程中穿件一個(gè)新的文件描述符蜓耻,并且該文件描述符和發(fā)送進(jìn)程中被傳遞的文件描述符指向內(nèi)核中相同的文件表項(xiàng)茫舶。
那么如何在兩個(gè)不相關(guān)的進(jìn)程間傳遞文件描述符?那就得用到Unix域的原始套接字在進(jìn)程間傳遞特殊的輔助數(shù)據(jù),以實(shí)現(xiàn)文件描述符的傳遞刹淌。
在Linux中使用socketpair函數(shù)創(chuàng)造一對(duì)未命名的饶氏、相互連接的UNIX域套接字。
完全可以把這一對(duì)socket當(dāng)成pipe返回的文件描述符一樣使用有勾,唯一的區(qū)別就是這一對(duì)文件描述符中的任何一個(gè)都可讀和可寫
#include <sys/types.h>
#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);
- 第1個(gè)參數(shù)domain疹启,表示協(xié)議族,只能為AF_LOCAL或者AF_UNIX蔼卡;
- 第2個(gè)參數(shù)type喊崖,表示類型,只能為0雇逞。
- 第3個(gè)參數(shù)protocol荤懂,表示協(xié)議,可以是SOCK_STREAM或者SOCK_DGRAM喝峦。用SOCK_STREAM建立的套接字對(duì)是管道流势誊,與一般的管道相區(qū)別的是呜达,套接字對(duì)建立的通道是雙向的谣蠢,即每一端都可以進(jìn)行讀寫。參數(shù)sv,用于保存建立的套接字對(duì)眉踱。
一次讀多個(gè)緩沖區(qū)
readv和writev函數(shù)用于在一次函數(shù)調(diào)用中讀挤忙、寫多個(gè)非連續(xù)緩沖區(qū)。有時(shí)也將這兩個(gè)函數(shù) 成為 散布讀(scatter read) 和 聚集寫(gather write) 谈喳。如果使用read或者 write册烈,完成同樣的功能需要多次的系統(tǒng)調(diào)用。現(xiàn)在用readv和writev主要調(diào)用一次就OK婿禽。
#include <sys/uio.h>
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
/* 成功時(shí)返回已讀赏僧、寫的字節(jié)數(shù),出錯(cuò)返回-1 */
控制文件描述屬性和行為
fcntl,ioctl
#include <fcntl.h>
int fcntl(int filedes, int cmd, ... /* struct flock *flockptr */ );
/* 成功的返回依賴cmd扭倾,出錯(cuò)返回-1 */
存儲(chǔ)映射IO
存儲(chǔ)映射IO(Memory-mapped IO) 使一個(gè)磁盤文件與存儲(chǔ)空間中的一個(gè)緩沖區(qū)映射淀零。 操作緩沖區(qū)就相當(dāng)于操作磁盤上的文件。 mmap 函數(shù)實(shí)現(xiàn)這個(gè)功能膛壹。
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flag, int filedes,
off_t off);
/* 若成功則返回映射區(qū)的起始地址驾中,若出錯(cuò)則返回MAP_FAILED */