首页 > 代码库 > 高级I/O函数(2)-splice函数

高级I/O函数(2)-splice函数

splice函数:

    功能描述:用于在两个文件描述符之间移动数据,也是零拷贝操作。函数定义如下:

1 #include <fcntl.h>3   ssize_t splice(int fd_in,loff_t* off_t,int fd_out,loff_t* off_out,size_t len,unsigned int flags);

参数描述:

    fd_in:待输入数据的文件描述符.

    off_t:如果fd_in是一个管道文件描述符,那么off_t参数必须是NULL,表示从数据流的当前偏移位置读入;如果fd_in不是一个管道文件描述符(例如socket),则它将指出具体的偏移位置.

    len:指定移动数据的长度.

    flags:则控制数据如何移动,它可以被设置为下表中值的按位异或.

          表  splice的flags参数的常用取值及其含义

             常用值含义
SPLICE_F_MOVE如果合适的话,按整页内存移动数据.
SPLICE_F_NONBLOCK非阻塞的splice操作,但实际效果还是会受文件描述符本身的阻塞状态的影响.
SPLICE_F_MORE给内核一个提示:后续的splice调用将读取更多的数据
SPLICE_F_GIFT对splice没有效果.

 注意:

    使用splice函数时,fd_in和fd_out必须至少有一个管道文件描述符.调用成功后返回移动字节的数量.它可能返回0,这发生从管道中读取数据时而该管道没有被写入任何数据.错误返回-1并设置errno.

 例子:利用splice函数来实现一个零拷贝的回射服务器模型。

 1 #include <sys/socket.h> 2 #include <netinet/in.h> 3 #include <arpa/inet.h> 4 #include <unistd.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <fcntl.h> 8  9 int main(int argc,const char* argv[]){10       if(argc!=2){11           printf("usage:%s ip_address port_number\n",argv[0]);12           return -1;13       }14       15       const char* ip=argv[1];16       int port=atoi(argv[2]);17       18       int ret;19       struct sockaddr_in address;20       bzero(&address,sizeof(address));21       address.sin_family=AF_INET;22       inet_pton(AF_INET,ip,&address.sin_addr);23       address.sin_port=htons(port);24       25       int sockfd=socket(AF_INET,SOCK_STREAM,0);26       assert(sockfd!=-1);27       28       ret=bind(sockfd,(struct sockaddr*)&address,sizeof(address));29       assert(ret!=-1);30       31       ret=listen(sockfd,5);32       assert(ret!=-1);33       34       while(1){35           struct sockaddr_in peer;36           bzero(&peer,sizeof(peer));37           socklen_t len=sizeof(peer);38           39           int connfd=accept(sockfd,(struct sockaddr*)&peer,len);40           if(connfd<0){41               printf("errno is:%d\n",errno);42               break;43           }44           else{45               int pipefd[2];46               ret=pipe(pipefd);47               assert(ret!=-1);48               49               /*将connfd上流入的客户端数据定向到管道中*/50               ret=splice(connfd,NULL,pipefd[1],NULL,32768,SPLICE_F_MORE|51                                           SPLICE_F_MOVE);52               assert(ret!=-1);53               /*将管道中的数据定向到connfd的客户端文件描述符上*/54               splice(pipefd[0],NULL,connfd,NULL,32768,SPLICE_F_MORE|55                                           SPLICE_F_MOVE);56               assert(ret!=-1);57            }          58       }59       60       close(connfd);61       close(sockfd);62       return 0;63 }

我们通过splice函数将从客户端的内容读入到pipefd[1]中,然后再使用splice函数从pipefd[0]中读出该内容到客户端。从而实现了简单高效的回射服务。整个过程为执行recv/send操作,因此未涉及用户空间和内核空间之间的数据拷贝。

   

    

     

高级I/O函数(2)-splice函数