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

   

    

     

你可能感兴趣的:(splice)