linux网络编程九:splice函数,高效的零拷贝

Posted 浪子回头jin不换

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux网络编程九:splice函数,高效的零拷贝相关的知识,希望对你有一定的参考价值。

from:http://blog.csdn.net/jasonliuvip/article/details/22600569

         linux网络编程九:splice函数,高效的零拷贝                     

最近在看《Linux高性能服务器编程》,在此做个日记,以激励自己,同时分享于有需要的朋友。


1. splice函数

 

[cpp] view plain copy
 
技术分享技术分享
  1. #include <fcntl.h>  
  2. ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);  


splice用于在两个文件描述符之间移动数据, 也是零拷贝。

 

fd_in参数是待输入描述符。如果它是一个管道文件描述符,则off_in必须设置为NULL;否则off_in表示从输入数据流的何处开始读取,此时若为NULL,则从输入数据流的当前偏移位置读入。

fd_out/off_out与上述相同,不过是用于输出。

len参数指定移动数据的长度。

flags参数则控制数据如何移动:

 

  • SPLICE_F_NONBLOCK:splice 操作不会被阻塞。然而,如果文件描述符没有被设置为不可被阻塞方式的 I/O ,那么调用 splice 有可能仍然被阻塞。
  • SPLICE_F_MORE:告知操作系统内核下一个 splice 系统调用将会有更多的数据传来。
  • SPLICE_F_MOVE:如果输出是文件,这个值则会使得操作系统内核尝试从输入管道缓冲区直接将数据读入到输出地址空间,这个数据传输过程没有任何数据拷贝操作发生。

 

2. 使用splice时, fd_in和fd_out中必须至少有一个是管道文件描述符。

调用成功时返回移动的字节数量;它可能返回0,表示没有数据需要移动,这通常发生在从管道中读数据时而该管道没有被写入的时候。

失败时返回-1,并设置errno


3. 代码:通过splice将客户端的内容读入到管道中, 再从管道中读出到客户端,从而实现高效简单的回显服务。整个过程未执行recv/send,因此也未涉及用户空间到内核空间的数据拷贝。

 

[cpp] view plain copy
 
技术分享技术分享
    1. //使用splice实现的回显服务器  
    2. #include <stdio.h>  
    3. #include <stdlib.h>  
    4. #include <unistd.h>  
    5. #include <sys/socket.h>  
    6. #include <netinet/in.h>  
    7. #include <arpa/inet.h>  
    8. #include <assert.h>  
    9. #include <errno.h>  
    10. #include <string.h>  
    11. #include <fcntl.h>  
    12.   
    13.   
    14. int main(int argc, char **argv)  
    15. {  
    16.   
    17.     if (argc <= 2) {  
    18.         printf("usage: %s ip port\n", basename(argv[0]));  
    19.         return 1;  
    20.     }  
    21.       
    22.     const char *ip = argv[1];  
    23.     int port = atoi(argv[2]);  
    24.   
    25.     struct sockaddr_in address;  
    26.     bzero(&address, sizeof(address));  
    27.     address.sin_family = AF_INET;  
    28.     address.sin_port = htons(port);  
    29.     inet_pton(AF_INET, ip, &address.sin_addr);  
    30.   
    31.     int sock = socket(PF_INET, SOCK_STREAM, 0);  
    32.     assert(sock >= 0);  
    33.       
    34.     int reuse = 1;  
    35.     setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));  
    36.   
    37.     int ret = bind(sock, (struct sockaddr*)&address, sizeof(address));  
    38.     assert(ret != -1);  
    39.   
    40.     ret = listen(sock, 5);  
    41.     assert(ret != -1);  
    42.       
    43.     struct sockaddr_in client;  
    44.     socklen_t client_addrlength = sizeof(client);  
    45.       
    46.     int connfd = accept(sock, (struct sockaddr*)&client, &client_addrlength);  
    47.     if (connfd < 0) {  
    48.         printf("errno is: %s\n", strerror(errno));  
    49.     }  
    50.     else {  
    51.         int pipefd[2];  
    52.                   
    53.         ret = pipe(pipefd);  //创建管道  
    54.         assert(ret != -1);  
    55.           
    56.                 //将connfd上的客户端数据定向到管道中  
    57.         ret = splice(connfd, NULL, pipefd[1], NULL,  
    58.                         32768, SPLICE_F_MORE | SPLICE_F_MOVE);  
    59.         assert(ret != -1);  
    60.           
    61.                 //将管道的输出定向到connfd上  
    62.         ret = splice(pipefd[0], NULL, connfd, NULL,  
    63.                         32768, SPLICE_F_MORE | SPLICE_F_MOVE);  
    64.         assert(ret != -1);                
    65.           
    66.         close(connfd);  
    67.     }  
    68.   
    69.       
    70.     close(sock);  
    71.   
    72.   
    73.   
    74.   
    75.     return 0;  
    76. }  




以上是关于linux网络编程九:splice函数,高效的零拷贝的主要内容,如果未能解决你的问题,请参考以下文章

splice()和slice()方法的区别

前端学习之函数式编程—纯函数

条目九《慎重选择删除元素的方法》

Linux中的零拷贝技术

Linux 中的零拷贝技术 转

Linux 网络编程详解九