【Linux编程】零拷贝之splice( )函数和tee( )函数

关于零拷贝技术的相关文章,请参考:【Linux编程】大冒险之零拷贝技术探究

splice( )函数

在两个文件描述符之间移动数据,同sendfile( )函数一样,也是零拷贝。
函数原型:

#include 
ssize_t splice(int fdin, loff_t *offin, int fdout, loff_t *offout, size_t len, unsigned int flags);

参数意义

fdin参数:待读取数据的文件描述符。
offin参数:指示从输入数据的何处开始读取,为NULL表示从当前位置。如果fdin是一个管道描述符,则offin必须为NULL。
fdout参数:待写入数据的文件描述符。
offout参数:同offin,不过用于输出数据。
len参数:指定移动数据的长度。
flags参数:表示控制数据如何移动,可以为以下值的按位或:

  • SPLICE_F_MOVE:按整页内存移动数据,存在bug,自内核2.6.21后,实际上没有效果。
  • SPLICE_F_NONBLOCK:非阻塞splice操作,实际会受文件描述符本身阻塞状态影响。
  • SPLICE_F_MORE:提示内核:后续splice将调用更多数据。
  • SPLICE_F_GIFT:对splice没有效果。

fdin和fdout必须至少有一个是管道文件描述符。

返回值

返回值>0:表示移动的字节数。
返回0:表示没有数据可以移动,如果从管道中读,表示管道中没有被写入数据。
返回-1;表示失败,并设置errno。

errno值如下

  • EBADF:描述符有错。
  • EINVAL:目标文件不支持splice,或者目标文件以追加方式打开,或者两个文件描述符都不是管道描述符。
  • ENOMEM:内存不够。
  • ESPIPE:某个参数是管道描述符,但其偏移不是NULL。

tee( )函数

在两个管道文件描述符之间复制数据,同是零拷贝。但它不消耗数据,数据被操作之后,仍然可以用于后续操作。
函数原型:

#include 
ssize_t tee(int fdin, int fdout, size_t len, unsigned int flags);

参数意义

fdin参数:待读取数据的文件描述符。
fdout参数:待写入数据的文件描述符。
len参数:表示复制的数据的长度。
flags参数:同splice( )函数。

fdin和fdout必须都是管道文件描述符。
返回值

返回值>0:表示复制的字节数。
返回0:表示没有复制任何数据。
返回-1:表示失败,并设置errno。

代码实例:

/*splice()和tee()实现将文件"./1.txt"同时拷贝到文件"./2.txt"和"./3.txt"中*/
#include 
#include 
#include 
#include 
#include 

int main(){
    int fd1 = open("./1.txt", O_RDONLY);
    int fd2 = open("./2.txt", O_RDWR| O_CREAT | O_TRUNC, 0666);
    int fd3 = open("./3.txt", O_RDWR| O_CREAT | O_TRUNC, 0666);

    /*用于向"./2.txt"输入数据*/
    int pipefd2[2];
    /*用于向"./3.txt"输入数据*/
    int pipefd3[2];
    pipe(pipefd2);
    pipe(pipefd3);

    /*将fd1文件的内容输入管道pipefd2中*/
    splice(fd1, NULL, pipefd2[1], NULL, 10086, SPLICE_F_MORE);
    /*将管道pipefd2的内容复制到管道pipefd3中,不消耗管道pipefd2上的数据,管道pipefd2上的数据可以用于后续操作*/
    tee(pipefd2[0], pipefd3[1], 10086, SPLICE_F_NONBLOCK);
    /*将管道pipefd2的内容写入fd2文件中*/
    splice(pipefd2[0], NULL, fd2, NULL, 10086, SPLICE_F_MORE);
    /*将管道pipefd3的内容写入fd3文件中*/
    splice(pipefd3[0], NULL, fd3, NULL, 10086, SPLICE_F_MORE);

    close(fd1);
    close(fd2);
    close(fd3);
    close(pipefd2[0]);
    close(pipefd2[1]);
    close(pipefd3[0]);
    close(pipefd3[1]);
    return 0;
}

为了简化操作,程序里省略了错误处理,实际编程中需要加入。

相关文件:
【Linux编程】大冒险之零拷贝技术探究

你可能感兴趣的:(Linux网络编程)