Linux | 高级I/O函数
文章目錄
- 創(chuàng)建文件描述符的函數(shù)
- pipe函數(shù)
- dup函數(shù)、dup2函數(shù)
- 讀取或?qū)懭霐?shù)據(jù)
- readv函數(shù)、writev函數(shù)
- 零拷貝
- sendfile函數(shù)
- splice函數(shù)
- tee函數(shù)
- 進(jìn)程間通信——共享內(nèi)存
- mmap函數(shù) 和 munmap函數(shù)
- 控制文件描述符
- fcntl函數(shù)
創(chuàng)建文件描述符的函數(shù)
pipe函數(shù)
不再贅述,詳情見(jiàn)我的另一篇博客。
值得一提的是,socket 基礎(chǔ)API中有一個(gè) socketpair函數(shù),能夠方便地創(chuàng)建雙向管道:
#include<sys/types.h> #include<sys/socket.h> int socketpair(int domain, int type, int protocol, inf fd[2]); // domain只能使用 UNIX 本地域協(xié)議族 AF_UNIX,因?yàn)槲覀儍H能在本地使用這個(gè)雙向管道。 // 成功時(shí)返回0,失敗時(shí)返回-1并設(shè)置error。dup函數(shù)、dup2函數(shù)
這兩個(gè)函數(shù)會(huì)在 CGI服務(wù)器 中用到。CGI服務(wù)器: 主要是通過(guò)把服務(wù)器本地標(biāo)準(zhǔn)輸入、輸出或者文件重定向到網(wǎng)絡(luò)連接中,以達(dá)到向標(biāo)準(zhǔn)輸入、輸出緩沖區(qū)中輸入的信息,能在網(wǎng)絡(luò)連接中發(fā)送的效果。
#include<unistd.h> int dup( int file_descriptor ); int dup2( int file_descriptor_one, int file_descriptor_two );- dup: 創(chuàng)建一個(gè)新的文件描述符,該文件描述符和原有文件描述符 file_descriptor 指向相同的文件、管道或網(wǎng)絡(luò)連接。返回的文件描述符總是取系統(tǒng)當(dāng)前可用的最小整數(shù)值。
- dup2: 與 dup 類似,只是返回第一個(gè)不小于 file_descriptor_two 的整數(shù)值。
兩個(gè)系統(tǒng)調(diào)用失敗時(shí)都返回 -1,并設(shè)置 error。
通過(guò) dup 和 dup2 創(chuàng)建的文件描述符并不繼承原文件描述符的屬性。比如:close-on-exec 和 non-blocking 等。
用 dup 實(shí)現(xiàn)一個(gè)基本的 CGI服務(wù)器 的局部代碼:
close( STDOUT_FILENO ); dup( connfd ); printf( "hello\n" ); close( connfd );流程:
讀取或?qū)懭霐?shù)據(jù)
readv函數(shù)、writev函數(shù)
- readv函數(shù): 將數(shù)據(jù)從文件描述符讀到分散的內(nèi)存塊中,即分散讀;
- writev函數(shù): 將多塊分配的內(nèi)存數(shù)據(jù)一并寫入文件描述符中,即集中寫。
相當(dāng)于簡(jiǎn)化版的 recvmsg 和 sendmsg 。
#include<sys/uio.h> ssize_t readv( int fd, const struct iovec* vector, int count ); ssize_t weitev( int fd, const struct iovec* vector, int count ); // fd:被操作的目標(biāo)文件描述符 // vector:iovec結(jié)構(gòu)數(shù)組,iovec封裝了一塊內(nèi)存的起始位置和長(zhǎng)度 // count:vector數(shù)組的長(zhǎng)度,即有多少塊內(nèi)存數(shù)據(jù)需要從fd讀出或?qū)懭氲絝d // 成功時(shí)返回讀出/寫入fd的字節(jié)數(shù),失敗則返回-1并試著errno。零拷貝
sendfile函數(shù)
sendfile函數(shù): 用于在兩個(gè)文件描述符之間直接傳遞數(shù)據(jù)(完全在內(nèi)核中操作),從而避免了內(nèi)核緩沖區(qū)和用戶緩沖區(qū)之間的數(shù)據(jù)拷貝,效率很高。被稱為 零拷貝。
#include<sys/sendfile.h> ssize_t sendfile( int out_fd, int in_fd, off_t* offset, size_t count ); // out_fd:待寫入內(nèi)容的文件描述符,必須是一個(gè)socket // in_fd:待讀出內(nèi)容的文件描述符,必須是一個(gè)支持類似mmap函數(shù)的文件描述符,即必須指向真實(shí)的文件,不能是socket和管道 // offset:指定從讀入文件流的哪個(gè)位置開(kāi)始讀,如果為空,則使用讀入文件流默認(rèn)的起始位置 // count:傳輸?shù)淖止?jié)數(shù) // 成功時(shí)返回傳輸?shù)淖止?jié)數(shù),失敗返回-1,并設(shè)置errnosendfile 幾乎是專門為在網(wǎng)絡(luò)上傳輸文件而設(shè)計(jì)的。
splice函數(shù)
splice函數(shù): 本質(zhì)就是借助管道描述符在兩個(gè)文件之間移動(dòng)數(shù)據(jù),也是零拷貝。
#include<fcntl.h> ssize_t splice( int fd_in, loff_t* off_in, int fd_out, loff_t* off_out, size_t len, unsigned int flags ); // fd_in:待輸入數(shù)據(jù)的文件描述符 // off_in:如果fd_in是一個(gè)管道描述符,那么off_in必須被設(shè)置為NULL;反之,如果fd_in不是一個(gè)管道描述符(如socket),那么off_in表示從輸入數(shù)據(jù)流開(kāi)始讀取數(shù)據(jù)的起始位置。總而言之,若off_in不為NULL,則它將指出具體的偏移位置。 // fd_out/off_out:與fd_in/off_in類似,不過(guò)用于輸出數(shù)據(jù)流。 // len:移動(dòng)數(shù)據(jù)的長(zhǎng)度 // flags:控制數(shù)據(jù)的移動(dòng)方式使用 splic函數(shù) 時(shí) fd_in 和 fd_out 必須至少有一個(gè)是管道文件描述符。splice函數(shù) 調(diào)用成功時(shí)返回移動(dòng)字節(jié)的數(shù)量。可能返回 0,表示沒(méi)有數(shù)據(jù)需要移動(dòng),這發(fā)生在從管道中讀取數(shù)據(jù)(fd_in 是管道文件描述符)而管道沒(méi)有被寫入任何數(shù)據(jù)時(shí)(fd_out 不是管道文件描述符)。splice函數(shù)失敗時(shí)返回 -1 并設(shè)置 errno 。
常見(jiàn)的 errno :
| EBADF | 參數(shù)所指文件描述符有錯(cuò) |
| ENOMEM | 內(nèi)存不夠 |
| EINVAL | 目標(biāo)文件系統(tǒng)不支持splice,或者目標(biāo)文件以追加方式打開(kāi),或者兩個(gè)文件描述符都不是管道文件描述符,或者某個(gè) offset 參數(shù)被用于不支持隨機(jī)訪問(wèn)的設(shè)備(如字符設(shè)備) |
| ESPIPE | 參數(shù) fd_in(或fd_out) 是管道文件描述符,而 off_in(或off_out) 不為NULL |
tee函數(shù)
tee函數(shù): 兩個(gè)管道文件描述符之間的 零拷貝。它不消耗數(shù)據(jù),因此源文件描述符上的數(shù)據(jù)仍可以用于后續(xù)的讀操作。
#include<fcntl.h> ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags); // 參數(shù)含義同splice,但 fd_in 和 fd_out 必須同時(shí)是管道文件描述符 // 成功時(shí)返回兩個(gè)文件之間復(fù)制的數(shù)據(jù)數(shù)量(字節(jié)數(shù)),返回0表示沒(méi)有復(fù)制任何數(shù)據(jù),tee失敗返回-1并設(shè)置errno。tee 常和 splice 一起用,splice 將非管道與管道綁定,tee 將 splice 操作后得到的管道綁定在一起。
進(jìn)程間通信——共享內(nèi)存
mmap函數(shù) 和 munmap函數(shù)
- mmap函數(shù): 申請(qǐng)一段內(nèi)存空間作為進(jìn)程間通信的共享內(nèi)存,也可以將文件直接映射到其中。
- munmap函數(shù): 釋放由 mmap 創(chuàng)建的內(nèi)存空間。
mmap 的 flags 參數(shù)的常用值及其含義:
| MAP_SHARED | 進(jìn)程間共享這段內(nèi)存,對(duì)該內(nèi)存段的修改將反映到被映射的文件中。提供了進(jìn)程間共享內(nèi)存的 POSIX 方法 |
| MAP_PRIVATE | 內(nèi)存段為調(diào)用進(jìn)程所私有,所有修改不會(huì)反映到被映射的文件中 |
| MAP_ANONYMOUS | 這段內(nèi)存不是從文件映射而來(lái)的,其內(nèi)容被初始化為全0。此時(shí) mmap 最后兩個(gè)參數(shù)將被忽略。 |
| MAP_FIXED | 內(nèi)存段必須位于 start 指定的地址處,start 必須是內(nèi)存頁(yè)面大小(4096字節(jié)=4KB)的整數(shù)倍 |
| MAP_HUGETLB | 按照“大內(nèi)存頁(yè)面”來(lái)分配內(nèi)存空間,“大內(nèi)存頁(yè)面”由 /proc/meminfo 文件來(lái)擦查看 |
控制文件描述符
fcntl函數(shù)
fcntl函數(shù): 全名 file control ,提供了對(duì)文件描述符的各種控制操作(類似于系統(tǒng)調(diào)用 ioctl,但 ioctl 比 fcntl 提供的控制更多。)但是 fcntl 是 POSIX 規(guī)定的首選方法。
#include<fcntl.h> int fcntl(int fd, int cmd, ...); // fd:被操作的文件描述符 // cmd:執(zhí)行何種操作 // 有可能需要第三個(gè)可選參數(shù) arg // 成功時(shí)返回值根據(jù)操作不同有所不同,失敗時(shí)返回-1并設(shè)置errno將文件描述符設(shè)置為非阻塞的:
int setnonblocking( int fd ){// F_GETFL 獲取 fd 的標(biāo)志,成功時(shí)返回 fd 的標(biāo)志int old_option = fcntl(fd, F_GETFL); // 獲取文件描述符舊的狀態(tài)標(biāo)志int new_option = old_option | O_NONBLOCK; // 設(shè)置非阻塞標(biāo)志fcntl(fd, F_SETFL, new_option); // F_SETFL 設(shè)置 fd 的標(biāo)志return old_option; // 返回文件描述符舊的狀態(tài)標(biāo)志,以便日后恢復(fù)該狀態(tài)標(biāo)志 }題外話:SIGIO 和 SIGURG 這兩個(gè)信號(hào)與其他 Linux 信號(hào)不同,他們必須與某個(gè)文件描述符相關(guān)聯(lián)方可使用:
- 被關(guān)聯(lián)文件描述符可讀或可寫時(shí),系統(tǒng)將觸發(fā) SIGIO 信號(hào)。
- 被關(guān)聯(lián)文件描述符是一個(gè) socket 且有帶外數(shù)據(jù)可讀時(shí),系統(tǒng)將觸發(fā) SIGURG 信號(hào)。
這兩個(gè)信號(hào)就是通過(guò) fcntl函數(shù) 與文件描述符關(guān)聯(lián)的,具體做法是:fcntl函數(shù) 為目標(biāo)文件描述符指定宿主進(jìn)程或進(jìn)程組,被指定的宿主進(jìn)程或進(jìn)程組去捕獲這兩個(gè)信號(hào)。
特別的,使用 SIGIO 時(shí),還需利用 fcntl 設(shè)置其 O_ASYNC標(biāo)志(異步I/O標(biāo)志,不過(guò)SIGIO信號(hào)模型并非真正意義上的異步I/O模型)。
總結(jié)
以上是生活随笔為你收集整理的Linux | 高级I/O函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: leetcode620. 有趣的电影(S
- 下一篇: linux 其他常用命令