linux高性能服務器編程學習筆記五:高級I/O函數

1、linux提供了一些高級I/O函數,在特定的情況下表現出優秀的性能。這些函數大致分爲3類

(1)用於創建文件描述符的函數,包括pipe、dup/dup2函數

(2)用於讀寫數據的函數,包括readv/writev、sendfile、mmap/munmap、splice和tee函數

(3)用於控制I/O行爲和屬性的函數,包括fcntl函數。

2、pipe函數(intpipe(int fd[2]))

(1)pipe函數的參數是一個包含兩個int型整數的數組指針,函數成功時返回0,並將一對打開的文件描述符值填入其參數指向的數組。

(2)fd[0]表示進程可以從管道的fd[0]端讀數據,fd[1]表示進程可以寫數據進fd[1]端。

(3)若是這一對文件描述符都是阻塞的,那麼假如管道爲空,read調用將被阻塞直到有數據可以讀。假如管道滿了,調用write往管道寫數據會阻塞直到管道有空餘空間。

(4)若是這一對文件描述符是非阻塞的,那麼假如管道的讀端文件描述符fd[0]減少至0,即沒有任何進程需要從管道讀數據,這對該管道的write操作將失敗,並觸發SIGPIPE信號。假如管道的寫端fd[1]文件描述符減少至0,即沒有進程往管道寫數據,則對該管道的read操作將返回0,即讀取到了文件結束標記。

(5)管道內部傳輸的也是字節流,但是和TCP的字節流還是有細微的區別,應用程序能往一個TCP連接中寫入多少字節的數據取決於對端的接收窗口和本端的擁塞窗口。而管道本身有一個固定的容量限制,一般爲65536字節,也可以通過fcntl函數來修改。

(6)socket有個 socketpair函數,能夠很方便的創建雙向管道。並且創建的一對文件描述符都輸即可讀又可寫。函數原型爲:

int socketpair(int domain, inttype, int protocol, int fd[2])。注意domain只能爲UNIX本地域的AF_UNIX,因爲只能在本地使用雙向管道。

3、dup函數和dup2函數

(1)當希望把標準輸入重定向到一個文件,或者把標準輸出重定向到一個網絡連接,可以通過複製文件描述符的duo或dup2函數來實現。

int dup(int file_descriptor)

int dup2(intfile_descriptor_one, int file_descriptor_two)

(2)dup函數是創建一個新的文件描述符,和原有的文件描述符file_descriptor指向相同的文件、管道或者網絡連接。並且dup返回的文件描述符總是取系統當前可用的最小整數值。dup2和dup函數類似,但是返回的是不小於file_descriptor_two的整數值。

(3)注意:使用dup和dup2並不繼承原有文件描述符的屬性,例如close-on-exec以及non-blocking。

4、readv和writev函數

(1)readv函數將數據從文件描述符讀到分散的內存中,即分散讀,writev函數則將多塊分散的內存數據一併讀入到文件描述符當中,即集中寫。函數原型如下:

ssize_t readv(int fd,const struct iovec* vector,int count);

ssize_t writev(int fd, const struct iovec* vector,int count);

iovec結構成員有內存的初始地址和該內存的大小。count表示vector數組的長度,既有多少個iovec結構體類型數據。表示的是有多少塊內存數據需要從fd讀出或寫到fd。兩個函數調用成功返回讀出/寫入fd的字節數。失敗則返回-1並設置errno。相當於簡化版的recvmsg和sendmsg。

4、sendfile函數

ssize_t sendfile(int out_fd, int in_fd, off_t*offset,size_t count)

(1)in_fd參數是待讀出內容的文件描述符,此文件描述符必須指向真實的文件(支持mmap函數),也就是說不能是socket和管道。Out_fd是待寫入內容的文件描述符,此文件描述符必須是socket。因此此函數幾乎是專門爲在網絡上傳輸文件而設計的。整體含義是in_fd表示從文件讀取數據,out_fd表示往socket當中寫。

(2)sendfile函數在兩個文件描述符之間傳遞數據,但是其完全在內核中操作,從而避免了內核緩衝區和用戶空間緩衝區之間的數據拷貝,效率很高,這被稱爲零拷貝。

(3)offset參數指定從讀入文件描述符的哪個位置開始讀,如果爲空則使用讀入文件流默認的起始位置。函數成功返回傳輸的字節數,失敗返回-1並設置errno。

5、mmap函數和munmap函數

(1)mmap函數用於申請一段內存空間,可以將這段內存作爲進程間通信的共享內存,也可以將文件直接映射到其中。munmap函數釋放由mmap創建的內存空間。

void *mmap(void* start, size_t length, int prot,int flags, int fd,off_t offset)

int munmap(void* start,size_t length)

(2)start參數允許用戶使某個待定的地址作爲申請的內存的起始地址,若是被設置爲NULL,則由系統自動分配一個地址。Length指定內存段的長度。prot參數用來設置內存段的訪問權限。可以取以下幾個值的按位或

   PROT_READ,內存段可讀

   PROT_WRITE,內存段可寫

   PROT_EXEC,內存段可執行

   PROT_NONE, 內存段不能被訪問。

(3)flags參數控制內存段被修改後程序的行爲(MAP_SHARED和MAP_PRIVATE互斥,不能同時指定)


(4)fd是被映射文件的文件描述符,一般通過open函數獲得。Offset設置從文件的何處開始映射。

(5)mmap函數成功調用返回指向內存區域的指針,失敗則返回MAP_FAILED((void*)-1)並設置errno。munmap函數成功返回0,失敗則返回-1並設置errno。

6、splice函數

ssize_t splice(int fd_in, loff_t* off_in, intfd_out, loff_t* off_out, size_t len, unsigned int flags);

(1)整體含義是讀文件描述符fd_in所對應的文件或socket或管道,然後寫入文件描述符fd_out所對應的文件或socket或管道。並且,fd_in和fd_out必須至少有一個爲管道文件描述符,當爲管道文件描述符的時候,相應的off_in/off_out就必須爲空(NULL),若不是管道文件描述符且off_in/off_out爲NULL,表示從輸入數據流的當前偏移位置讀或者是寫入輸出數據流的當前偏移位置。len表示指定移動數據的長度。

(2)函數成功調用時返回移動字節的數量,可能返回0,返回0表示沒有數據需要移動,這通常發生在從管道中讀取數據(fd_in爲管道文件描述符)而該管道沒有寫入任何數據時。函數調用失敗則返回-1並設置errno。

7、tee函數

ssize_t tee(int fd_in, int fd_out, size_t len,unsigned int flags)

(1)tee函數在兩個管道文件描述符之間複製數據,也是零拷貝操作並且不消耗數據,因此源文件描述符的數據仍然可以用於後續的讀操作。

(2)注意,相應的兩個文件描述符參數必須爲管道文件描述符。成功調用則返回複製的數據大小。返回0表示沒有複製任何數據,失敗返回-1並設置errno。

8、fcntl函數

int fcntl(int fd, int cmd, …)

(1)fcntl函數提供了對文件描述符的各種控制操作(另一個ioctl函數比fcntl能夠執行更多的控制)。

(2)cmd參數指定任何類型的操作,根據操作類型的不同,該函數可能還需要第三個可選參數arg。



3)在網絡編程中,fcntl函數通常用來將一個文件描述符設置爲非阻塞的。見以下代碼

int setnonblocking(int fd)

{

int old_option = fcntl(fd,F_GETFL);

int new_option = old_option | O_NONBLOCK;

fcntl(fd,F_SETFL,new_option);

return old_option;                      //返回文件描述符舊的狀態標誌爲了能夠恢復該狀態標標誌

}

4)這裏重點講兩個比較特殊的信號,SIGIOSIGURG信號,這兩個信號必須與某個文件描述符相關聯方可使用。當被關聯的文件描述符可讀或者可寫時,系統將會觸發SIGIO信號。當被關聯的文件描述符(必須是一個socket)上有帶外數據時,系統將觸發SIGURG信號,將信號與文件描述符相關聯的方法,就是使用fcntl函數爲目標文件描述符指定宿主進程或進程組,那麼被指定宿主進程或進程組將捕獲這兩個信號。使用SIGIO還得利用fcntl設置其O_ASYNC標誌(異步I/O標誌,但是SIGIO並非真正意義上的異步I/O模型)。

發佈了35 篇原創文章 · 獲贊 1 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章