日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux 高级IO函数之sendfile splice tee

發布時間:2025/3/15 linux 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 高级IO函数之sendfile splice tee 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

sendfile函數在兩個文件描述符之間傳遞數據(完全在內核中操作),從而避免內核緩沖區和用戶緩沖區之間的數據拷貝,效率很高,這被稱為零拷貝。函數的定義如下:

#include<sys/sendfile.h>ssize_t sendfile(int out_fd,int in_fd , off_t* offset ,size_t count);

in_fd參數是待讀出內容的文件描述符,out_fd參數是待寫入內容的文件描述符。offset參數執行從讀入文件流的哪個位置開始讀,如果為空,則使用讀入文件流的默認起始位置。count參數指定在文件描述符in_fd和out_fd之間傳輸的字節數。sendfile成功時返回傳輸的字節數,失敗則返回-1并設置errno。該函數的man手冊明確指出,in_fd必須是一個支持mmap函數的文件描述符,即它必須指向真實的文件,而不能是socket和管道;則out_fd則必須是一個socket。由此可見,sendfile幾乎是專門為在網絡上傳輸文件而設計的。

下面的例子利用sendfile函數將服務器上的一個文件傳送給客戶端

#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/sendfile.h>int main( int argc, char* argv[] ) {if( argc <= 3 ){printf( "usage: %s ip_address port_number filename\n", basename( argv[0] ) );return 1;}const char* ip = argv[1];int port = atoi( argv[2] );const char* file_name = argv[3];int filefd = open( file_name, O_RDONLY );assert( filefd > 0 );struct stat stat_buf;fstat( filefd, &stat_buf );struct sockaddr_in address;bzero( &address, sizeof( address ) );address.sin_family = AF_INET;inet_pton( AF_INET, ip, &address.sin_addr );address.sin_port = htons( port );int sock = socket( PF_INET, SOCK_STREAM, 0 );assert( sock >= 0 );int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );assert( ret != -1 );ret = listen( sock, 5 );assert( ret != -1 );struct sockaddr_in client;socklen_t client_addrlength = sizeof( client );int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );if ( connfd < 0 ){printf( "errno is: %d\n", errno );}else{sendfile( connfd, filefd, NULL, stat_buf.st_size );close( connfd );}close( sock );return 0; }

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參數是待輸入數據的文件描述符。如果fd_in是一個管道文件描述符,那么off_in參數必須設置為NULL。如果fd_in不是一個管道文件(比如是一個socket),那么off_in表示從輸入數據流的何處開始讀取數據。此時,如果off_in被設置為NULL,則表示從輸入數據流的當前偏移位置讀入;若off_in不為NULL,則它將指出具體的偏移位置。fd_out/off_out參數的含義與fd_in/off_in相同,不過用于輸出數據流。len參數指定移動數據的長度;flag參數則控制數據如何移動,它可以設置為下表中的某些值的按位或。


使用splice函數時,fd_in 和fd_out必須至少有一個是管道文件描述符。splice函數調用成功時返回移動字節的數量,它可能返回0,表示沒有數據需要移動,這發生在從管道中讀取數據,而該管道沒有被寫入任何數據時。spice函數失敗時返回-1并設置errno.常見的errno如下圖


下面的例子是利用splice函數來實現一個零拷貝的回射服務器,它將客戶端發送的數據原樣返回客戶端。

#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <fcntl.h>int main( int argc, char* argv[] ) {if( argc <= 2 ){printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );return 1;}const char* ip = argv[1];int port = atoi( argv[2] );struct sockaddr_in address;bzero( &address, sizeof( address ) );address.sin_family = AF_INET;inet_pton( AF_INET, ip, &address.sin_addr );address.sin_port = htons( port );int sock = socket( PF_INET, SOCK_STREAM, 0 );assert( sock >= 0 );int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );assert( ret != -1 );ret = listen( sock, 5 );assert( ret != -1 );struct sockaddr_in client;socklen_t client_addrlength = sizeof( client );int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );if ( connfd < 0 ){printf( "errno is: %d\n", errno );}else{int pipefd[2];assert( ret != -1 );ret = pipe( pipefd );ret = splice( connfd, NULL, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE ); assert( ret != -1 );ret = splice( pipefd[0], NULL, connfd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );assert( ret != -1 );close( connfd );}close( sock );return 0; }

我們通過splice函數將客戶端的內容讀取到pipefd[1]中,然后在使用splice函數從pipefd[0]中讀出該內容到客戶端,從而實現了簡單高效的回射服務。整個過程未執行recv/send操作,因此也未涉及用戶空間和內核空間之間的拷貝。

tee函數在兩個管道文件描述符之間復制數據,也是零拷貝操作。它不消耗數據,因此源文件描述符上的數據仍然可以用于后續的讀操作。函數原型如下:

#include<fcntl.h>ssize_t tee(int fd_in ,int fd_out,size_t len ,unsigned int flags);

該函數的參數的含義以splice相同(但fd_in 和fd_out都必須是管道文件描述符)。tee函數成功時返回在兩個文件描述符之間復制的數據數量(字節數)。返回0表示沒有復制任何數據,tee失敗時返回-1并設置errno。

如下代碼利用tee函數和splice函數,實現了linux下的tee程序(同時輸出數據到終端和文件的程序)

#include <assert.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <fcntl.h>int main( int argc, char* argv[] ) {if ( argc != 2 ){printf( "usage: %s <file>\n", argv[0] );return 1;}int filefd = open( argv[1], O_CREAT | O_WRONLY | O_TRUNC, 0666 );assert( filefd > 0 );int pipefd_stdout[2];int ret = pipe( pipefd_stdout );assert( ret != -1 );int pipefd_file[2];ret = pipe( pipefd_file );assert( ret != -1 );//close( STDIN_FILENO );// dup2( pipefd_stdout[1], STDIN_FILENO );//write( pipefd_stdout[1], "abc\n", 4 );ret = splice( STDIN_FILENO, NULL, pipefd_stdout[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );assert( ret != -1 );ret = tee( pipefd_stdout[0], pipefd_file[1], 32768, SPLICE_F_NONBLOCK ); assert( ret != -1 );ret = splice( pipefd_file[0], NULL, filefd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );assert( ret != -1 );ret = splice( pipefd_stdout[0], NULL, STDOUT_FILENO, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );assert( ret != -1 );close( filefd );close( pipefd_stdout[0] );close( pipefd_stdout[1] );close( pipefd_file[0] );close( pipefd_file[1] );return 0; }

總結

以上是生活随笔為你收集整理的linux 高级IO函数之sendfile splice tee的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。