Linux笔记之使用系统调用sendfile高速拷贝文件
code review!
文章目录
- Linux笔记之使用系统调用sendfile高速拷贝文件
-
- [`sendfile` 性能优势](#
sendfile
性能优势) - [`sendfile` 系统调用](#
sendfile
系统调用) - [`cp` 命令](#
cp
命令) - 实际测试:拷贝5.8个G的文件,cp使用25s,sendfile只用7.49s,快了四倍
- [`sendfile` 性能优势](#
sendfile
系统调用是 Linux 特有的函数,用于在两个文件描述符之间直接传输数据。这可以减少用户态和内核态之间的数据拷贝,从而提高文件拷贝的性能。sendfile
通常比使用普通的 read
和 write
系统调用更快,尤其是在处理大文件时。
sendfile
性能优势
- 减少数据拷贝 :
sendfile
在内核内直接把数据从一个文件描述符传输到另一个文件描述符,而不需要将数据从内核态拷贝到用户态再拷贝回内核态。 - 减少系统调用的开销 :对于大文件拷贝,
sendfile
只需要一次系统调用,而传统方法可能需要多次read
和write
调用。 - 更少的上下文切换 :由于减少了系统调用次数,
sendfile
也减少了用户态和内核态之间的上下文切换。
sendfile
系统调用
sendfile
它主要用于网络传输,但也可以用于文件复制。sendfile
的设计目的是减少数据复制的次数,从而提高性能。
优点:
- 减少上下文切换:
sendfile
可以直接在内核空间处理数据传输,避免了用户空间和内核空间之间的上下文切换。 - 高效: 它可以减少内存拷贝的次数,因为数据不需要从内核空间拷贝到用户空间再回到内核空间。
- 适合大文件传输: 对于大文件传输,
sendfile
可以显著减少 CPU 占用和提高传输速度。
缺点:
- 灵活性较低:
sendfile
主要用于文件描述符之间的传输,不如一些用户空间的工具灵活。 - 依赖于内核版本:
sendfile
的性能和功能可能会受到内核版本的影响。
cp
命令
cp
是一个用户空间工具,用于复制文件和目录。它使用标准的文件 I/O 操作来实现文件复制。
优点:
- 易用性:
cp
命令简单易用,支持各种选项,可以处理复杂的文件复制需求。 - 广泛支持: 几乎所有的 Unix/Linux 系统都支持
cp
命令,且不依赖于特定的内核版本。 - 灵活:
cp
命令支持递归复制、保留文件属性等多种操作。
缺点:
- 性能较低: 由于
cp
命令在用户空间运行,涉及多次上下文切换和内存拷贝,性能可能不如sendfile
。 - 高 CPU 使用率: 对于大文件复制,
cp
命令可能会占用较高的 CPU 资源。
实际测试:拷贝5.8个G的文件,cp使用25s,sendfile只用7.49s,快了四倍
代码
cpp
#include <sys/sendfile.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <chrono>
#include <sys/stat.h>
bool copyFileSendfile(const std::string& src, const std::string& dest) {
int source = open(src.c_str(), O_RDONLY);
int destination = open(dest.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (source < 0 || destination < 0) {
std::cerr << "Error opening files!" << std::endl;
return false;
}
struct stat stat_buf;
fstat(source, &stat_buf);
off_t offset = 0;
auto start = std::chrono::high_resolution_clock::now();
if (sendfile(destination, source, &offset, stat_buf.st_size) == -1) {
std::cerr << "Error using sendfile!" << std::endl;
close(source);
close(destination);
return false;
}
auto end = std::chrono::high_resolution_clock::now();
close(source);
close(destination);
std::chrono::duration<double> duration = end - start;
std::cout << "Sendfile copy took " << duration.count() << " seconds" << std::endl;
return true;
}
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " <source_file> <destination_file>" << std::endl;
return 1;
}
std::string sourceFile = argv[1];
std::string destinationFile = argv[2];
if (copyFileSendfile(sourceFile, destinationFile)) {
std::cout << "File copied successfully!" << std::endl;
} else {
std::cerr << "File copy failed!" << std::endl;
return 1;
}
return 0;
}
编译
g++ main.cpp -o main