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