零复制技术及Linux系统编程之sendfile函数的使用

一、 零复制技术

零复制(英语:Zero-copy;也译零拷贝)技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省CPU周期和内存带宽。

操作系统某些组件(例如驱动程序、文件系统和网络协议栈)若采用零复制技术,则能极大地增强了特定应用程序的性能,并更有效地利用系统资源。通过使CPU得以完成其他而非将机器中的数据复制到另一处的任务,性能也得到了增强。另外,零复制操作减少了在用户空间与内核空间之间切换模式的次数。举例来说,如果要读取一个文件并通过网络发送它,传统方式下每个读/写周期都需要复制两次数据和切换两次上下文,而数据的复制都需要依靠CPU。通过零复制技术完成相同的操作,上下文切换减少到两次,并且不需要CPU复制数据。

零复制协议对于网络链路容量接近或超过CPU处理能力的高速网络尤为重要。在这种网络下,CPU几乎将所有时间都花在复制要传送的数据上,因此将成为使通信速率低于链路容量的瓶颈。

二、 Linux系统sendfile函数

实现零复制的软件通常依靠基于直接存储器访问(DMA)的复制,以及通过内存管理单元(MMU)的内存映射。这些功能需要特定硬件的支持,并通常涉及到特定存储器的对齐。Linux内核通过各个系统调用支持零复制,例如sys/socket.h的sendfile、sendfile64以及splice。

#include <sys/sendfile.h>

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
// sendfile - transfer data between file descriptors

三、 利用sendfile函数实现文件复制

1、 示例代码

#include <unistd.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

int main(void)
{
	int in_fd, out_fd;
	struct stat buf;

	if((in_fd = open("copy_file.c", O_RDONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
	{
		printf("open file error\n");
		return 1;
	}

	if((out_fd = open("demo.c", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
	{
		printf("open file error\n");
		return 1;
	}

	fstat(in_fd, &buf);

	sendfile(out_fd, in_fd, NULL, buf.st_size);

	close(in_fd);
	close(out_fd);

	return 0;
}

2、 运行结果

[ycxie@fedora Workspace]$ gcc copy_file.c -o copy_file -Wall
[ycxie@fedora Workspace]$ ./copy_file
[ycxie@fedora Workspace]$ cat demo.c
#include <unistd.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

int main(void)
{
	int in_fd, out_fd;
	struct stat buf;

	if((in_fd = open("copy_file.c", O_RDONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
	{
		printf("open file error\n");
		return 1;
	}

	if((out_fd = open("demo.c", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
	{
		printf("open file error\n");
		return 1;
	}

	fstat(in_fd, &buf);

	sendfile(out_fd, in_fd, NULL, buf.st_size);

	close(in_fd);
	close(out_fd);

	return 0;
}
-rw-rw-r-- 1 ycxie ycxie       577 Jun  8 12:11 copy_file.c
-rw-r--r-- 1 ycxie ycxie       577 Jun  8 12:11 demo.c

Leave a Reply

Your email address will not be published. Required fields are marked *