linux高级I/O函数splice与tee

splice()

splice函数是linux系统提供的高级I/O函数,同sendfile系统调用函数一样,也是零拷贝操作函数。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参数是待输入描述符。如果它是一个管道文件描述符,则off_in必须设置为NULL;如果off_in不是一个管道文件描述符(比如socket),那么off_in表示从输入数据流的何处开始读取数据,此时若为NULL,则从输入数据流的当前偏移位置读入。
  • fd_out/off_out参数的含义与fd_in/off_in相同,不过用于输出数据流。
  • len参数指定移动数据的长度。
  • flags参数则控制数据如何移动,它可以设置成下表中的某些值的按位或。

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

常用值含义
SPLICE_F_MOVE如果合适的话,按整页内存移动数据。这只是给内核的一个提示。不过,因为它的实现存在BUG,所以自内核2.6.21后,它实际上没有任何效果
SPLICE_F_NONBLOCK非阻塞的splice操作,但实际效果还会受文件描述符本身的阻塞状态影响
SPLICE_F_MORE告知操作系统内核下一个 splice 系统调用将会有更多的数据传来
SPLICE_F_GIFT对splice没有效果

注意:使用splice函数时,fd_in和fd_out必须至少有一个是管道文件描述符。


函数返回值:

调用成功时返回移动的字节数量。它可能返回0,表示没有数据需要移动,这通常发生在从管道中读数据(fd_in是管道文件描述符)而该管道没有被写入任何数据时。
失败时返回-1,并设置errno。常见的errno如下表所示。

splice函数可能产生的errno及其含义:

错误含义
EBADF参数所指文件描述符有错
EINVAL目标文件系统不支持splice,或者目标文件以追加方式打开,或者两个文件描述符都不是管道文件描述符,或者某个offset参数被用于不支持随机访问的设备(比如字符设备)
ENOMEM内存不够
ESPIPE参数fd_in(或fd_out)是管道文件描述符,而off_in(或off_out)不为NULL

使用splice函数实现简单的回射服务器:

我们通过使用splice函数,实现一个简单的echo服务器。

//使用splice实现的回显服务器  
#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <assert.h>  
#include <errno.h>  
#include <string.h>  
#include <fcntl.h>  


int main(int argc, char **argv)  
{  

    if (argc <= 2) {  
        printf("usage: %s ip port\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;  
    address.sin_port = htons(port);  
    inet_pton(AF_INET, ip, &address.sin_addr);  

    int sock = socket(PF_INET, SOCK_STREAM, 0);  
    assert(sock >= 0);  

    int reuse = 1;  
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));  

    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: %s\n", strerror(errno));  
    }  
    else {  
        int pipefd[2];  

        ret = pipe(pipefd);  //创建管道  
        assert(ret != -1);  

        //将connfd上流入的客户端数据定向到管道中  
        ret = splice(connfd, NULL, pipefd[1], NULL,  
                        32768, SPLICE_F_MORE | SPLICE_F_MOVE);  
        assert(ret != -1);  

        //将管道的输出定向到connfd连接文件符上  
        ret = splice(pipefd[0], NULL, connfd, NULL,  
                        32768, SPLICE_F_MORE | SPLICE_F_MOVE);  
        assert(ret != -1);                

        close(connfd);  
    }  

    close(sock);  

    return 0;  
} 


tee()

tee函数在两个管道文件描述符之间复制数据,也是零拷贝操作。与splice函数不同,它不消耗数据,因此源文件描述符上的数据仍然可以用于后续的读操作。

函数原型:

#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。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值