ゼロコピーテクノロジー (DMA、MMAP、sendfile)

ゼロコピー mmap、sendfile

意味

ゼロコピー テクノロジは主に、従来のネットワーク I/O 操作におけるファイル送信のパフォーマンスの問題を解決することを目的としています。次の図は、読み取りおよび書き込み中の従来の I/O に関係する CPU 操作を示しています。

ここに画像の説明を挿入

  • これには 4 つのユーザー モード ↔ カーネル モード コンテキスト スイッチが含まれ、そのうち読み取りスイッチが 2 回、書き込みスイッチが 2 回行われます。
  • データのコピーが 4 つ含まれます。そのうち、DMA は 2 回コピーし、CPU は 2 回コピーします。

複数のコンテキストの切り替えと上記の操作のコピーは、パフォーマンスに影響します。

ゼロコピーテクノロジーを使用しmmap+write最適化できます。sendfilesplice

DMA

DMA (Direct Memory Access)、つまりダイレクト メモリ アクセスはデータを高速に転送するためのメカニズムです。データ転送に使用する場合、CPU の参加は必要ありません

DMA を使用してデータをコピーすると、CPU パラメータなしでデータ転送用のシステム データ バス リソースの一部が取得されます。IO 読み取りでも割り込みは発生しません。CPU は IO 操作を読み取り、システム コールによって割り込みが発生します。

mmap

mmap (メモリ マップ) は、仮想メモリとアドレス マッピングを使用して 1 つのコピーを削減します。カーネル モードからユーザー モードにデータをコピーする際のパフォーマンスの消費を削減できます。

ここに画像の説明を挿入

上図に示すように、スレーブデータはカーネルステートからユーザーステートにコピーされるのではなく、メモリマッピングを通じて送信するファイルの仮想メモリアドレスを直接取得して送信することができます。共有仮想メモリ アドレスを通じて転送され、情報はソケット バッファにコピーされて送信されます。

#include <sys/mman.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>

int main() {
    
    
    // 打开文件并将其映射到内存中
    int fd = open("file.txt", O_RDONLY);
    size_t size = lseek(fd, 0, SEEK_END);
    char* data = (char*) mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);

    // 创建套接字并连接到目标地址
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(1234);
    connect(sock, (sockaddr*) &addr, sizeof(addr));

    // 将内存中的数据直接写入套接字
    write(sock, data, size);//

    // 关闭套接字和文件,并解除内存映射
    close(sock);
    munmap(data, size);
    close(fd);

    return 0;
}

ここでの mmap はコピーを減らすだけですが、それでも 4 つのコンテキスト スイッチが必要です。それで、コンテキストスイッチを減らす方法はありますか。そうすれば、sendfileが登場します。

sendfile

sendfile を使用すると、ファイル送信時のコンテキストの切り替えを減らすことができます。

Linux バージョン 2.1 以降、sendfile操作を簡素化するために Linux が導入されました。sendfileさらに最適化するには、この方法を上記の方法に置き換えることができますmmap/write

sendfile以下をせよ:

  mmap();
  write();

替换为:

 sendfile();

writeこれにより、操作を開始するアプリケーションが 1 つ減り、sendfile操作を直接開始できるため、コンテキストの切り替えが減少します。

ここに画像の説明を挿入

ユーザーモードを介さず、カーネルモードでディスクデータをDMA経由でバッファ領域に直接コピーし、ソケットバッファ領域にバッファデータをコピーします。

#include <sys/socket.h>
#include <fcntl.h>
#include <cstring>

int main() {
    
    
    // 打开文件并获取文件描述符
    int fd = open("file.txt", O_RDONLY);

    // 创建套接字并连接到目标地址
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(1234);
    connect(sock, (sockaddr*) &addr, sizeof(addr));

    // 使用sendfile函数将文件信息发送到套接字
    off_t offset = 0;
    struct stat stat_buf;
    fstat(fd, &stat_buf);
    sendfile(sock, fd, &offset, stat_buf.st_size);//直接使用sendfile发送文件,避免上下文切换

    // 关闭套接字和文件
    close(sock);
    close(fd);

    return 0;
}

sendfile では 3 回のコピー アクションが実行されており、ユーザー モードとカーネル モードの間で頻繁に状態が切り替えられていないことがわかります。

sendfile は完璧ですが、CPU のコピーも保存できますか?

スキャッター/ギャザーを使用した sendfile

Linux 2.4 カーネルは最適化されており、最後のものを削除scatter/gatherできる sendfile 操作を提供します。CPU COPY原則として、読み取りバッファとソケット バッファはカーネル空間内のデータをコピーせず、読み取りバッファのメモリ アドレスとオフセットを対応するソケット バッファに記録するため、コピーは必要ありません。その本質は、メモリアドレスの記録である仮想メモリのソリューションと一致しています。

次の図は、scatter/gather の sendfile の原理を示しています。

ここに画像の説明を挿入

スプライス

違いは、ファイルとデータの転送だけでなく、任意の 2 つのファイルを相互に接続できることsendfileですsplicesocket

ファイル記述子との間でデータを送受信するsocket特殊な場合には、sendfile常にシステム コールが使用されてきました。

そしてspliceそれは常に単なるメカニズムであり、sendfile機能に限定されません。つまり、sendfile は splice のサブセットです。

sendfile とは異なり、splice はハードウェアのサポートを必要としません。

詳細は
https://juejin.cn/post/6995519558475841550
https://juejin.cn/post/7126195733471952910をご参照ください。

おすすめ

転載: blog.csdn.net/weixin_44477424/article/details/131774100
おすすめ