APUE编程:30---高级I/O之(数据零拷贝:splice函数)

一、函数介绍

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#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输出到fd_out

参数

  • fd_in:待输入数据的文件描述符
  • off_in:表示从输入数据流的何处开始读取数据,如果从输入数据流的当前偏移位置读入,那么此参数设置为NULL(如果fd_in是一个管道文件描述符,那么off_in必须设置为NULL)
  • fd_out:待输出数据的文件描述符
  • off_out:表示接收的数据存放到何处,如果存放到当前偏移位置,那么此参数设置为NULL(如果fd_out是一个管道文件描述符,那么off_out必须设置为NULL)
  • len:指定移动数据的长度
  • flags:控制数据如何移动,为下面的某些值的按位或
常用值含义
SPLICE_F_MOVE如果合适的话,按整页内存移动数据。这只是给内核的一个提示。不过,因为它的实现存在BUG,所以子内核2.6.21后,它实际上没有任何效果
SPLICE_F_NONBLOCK非阻塞splice操作,但实际效果还会受到文件描述符本身的阻塞状态的影响
SPLICE_F_MORE给内核的一个提示;后续的splice调用将读取更多数据
SPLICE_F_GIFT对splice没有效果

返回值

  • 执行成功返回移动字节的数量
  • 返回0:表示没有数据需要移动,这发生在从管道中读取数据(fd_in是管道文件描述符)而该管道没有被写入任何数据时
  • 失败:返回-1并设置errno,常见的errno如下
错误含义
EBADF参数所指文件描述符出错
EINVAL目标文件系统不支持splice,或者目标文件以追加方式打开,或者两个文件描述符都不是管道文件描述符,或者某个offset参数被用于不支持随机访问的设备(如字符设备)
ENOMEM内存不够
ESPIPE参数fd_in或fd_out是管道文件描述符,而off_in或off_out不为NULL

注意事项

  • 使用此函数时,fd_in和fd_out必须至少有一个是管道文件描述符
  • 宏_GNU_SOURCE必须定义在头文件fcntl的前面,否则编译会出错

二、演示案例

  • 下面使用splice函数来实现一个零拷贝的回射服务器,它将客户端发送的数据原样返回给客户端
//splice_serv.c

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc,char *argv[])
{
    if(argc!=3){
        printf("usage:%s ip port\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    int serv_fd,cli_fd;
    pid_t pid;
    struct sockaddr_in serv_addr;

    
    bzero(&serv_addr,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=htons(atoi(argv[2]));
    if(inet_pton(AF_INET,argv[1],&serv_addr.sin_addr)==-1){
        perror("inet_pton:");
        exit(EXIT_FAILURE);
    }

    if((serv_fd=socket(AF_INET,SOCK_STREAM,0))==-1){
        perror("inet_pton:");
        exit(EXIT_FAILURE);
    }

    if(bind(serv_fd,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1){
        perror("bind:");
        exit(EXIT_FAILURE);
    }

    if(listen(serv_fd,10)==-1){
        perror("listen:");
        exit(EXIT_FAILURE);
    }
    
    while(1){
        if((cli_fd=accept(serv_fd,NULL,NULL))==-1){
            continue;
        }
        if((pid=fork())==0){
            close(serv_fd);

            int pipe_arr[2];
            if(pipe(pipe_arr)==-1){
                perror("pipe:");
                exit(EXIT_FAILURE);
            }
            
            if(splice(cli_fd,NULL,pipe_arr[1],NULL,1024,SPLICE_F_MORE|SPLICE_F_MOVE)==-1){
                perror("splice:");
                exit(EXIT_FAILURE);
            }

            if(splice(pipe_arr[0],NULL,cli_fd,NULL,1024,SPLICE_F_MORE|SPLICE_F_MOVE)==-1){
                perror("splice:");
                exit(EXIT_FAILURE);
            }
            
            printf("splice success\n");
            
            close(cli_fd);
            exit(EXIT_SUCCESS);
        }
        close(cli_fd);
    }

    exit(EXIT_SUCCESS);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值