高级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操作,因此未涉及用户空间和内核空间之间的数据拷贝。

   

    

     

posted @ 2014-09-15 18:09  晓风_7  阅读(1421)  评论(1编辑  收藏  举报