Linux でのいくつかのゼロコピー テクノロジと適用可能なシナリオについて話しましょう

青灯夜游
リリース: 2020-07-27 17:40:01
転載
3323 人が閲覧しました

Linux でのいくつかのゼロコピー テクノロジと適用可能なシナリオについて話しましょう

この記事では、主要なゼロコピー テクノロジと、Linux でゼロコピー テクノロジが適用されるシナリオについて説明します。ゼロコピーの概念を迅速に確立するために、一般的に使用されるシナリオを紹介します。

#引用

##実行時のサーバーの書き込みプログラム (Web サーバーまたはファイル サーバー) では、ファイルのダウンロードが基本的な機能です。この時点でのサーバーのタスクは次のとおりです。

接続されたソケットからサーバーのホスト ディスク内のファイルを変更せずに送信します。 、通常は次のコードを使用して完了します。

while((n = read(diskfd, buf, BUF_SIZE)) > 0)
    write(sockfd, buf , n);
ログイン後にコピー
基本操作ファイルの内容をディスクからバッファにループで読み取り、バッファの内容を

socket に送信します。ただし、Linux の I/O 操作はデフォルトでバッファリングされた I/O であるためです。ここで使用される 2 つの主なシステム コールは readwrite ですが、オペレーティング システムがそれらの中で何を行うかはわかりません。実際、上記の I/O 操作では、複数のデータ コピーが発生しました。

アプリケーションが特定のデータにアクセスすると、オペレーティング システムはまず、そのファイルが最近アクセスされたかどうか、およびファイルの内容がカーネル バッファにキャッシュされているかどうかを確認します。キャッシュされている場合は、オペレーティング システムは直接読み取ります。

read によると、システム コールによって提供される buf アドレスは、カーネル バッファの内容を buf で指定されたユーザー空間バッファにコピーします。そうでない場合、オペレーティング システムはまずディスク上のデータをカーネル バッファにコピーします。このステップは現在主に DMA を使用して送信し、その後カーネル バッファの内容をユーザー バッファにコピーします。 次に、
write システム コールはユーザー バッファの内容をネットワーク スタックに関連するカーネル バッファにコピーし、最後に socket がカーネル バッファの内容を on に送信します。ネットワークカード。 ここまで言いましたが、より明確にするために写真を見たほうがよいでしょう:

Linux でのいくつかのゼロコピー テクノロジと適用可能なシナリオについて話しましょうLinux でのいくつかのゼロコピー テクノロジと適用可能なシナリオについて話しましょう

上の写真からわかるように、合計 4 つのデータ コピーが生成されます。ハードウェアとの通信を処理するために
DMA が使用されている場合でも、CPU は依然として 2 つのデータ コピーを処理する必要があります。同時に、ユーザー モードで複数のコンテキスト スイッチが発生し、カーネル モードでは、間違いなく CPU の負荷が増大します。

このプロセス中、ファイルの内容には一切変更を加えなかったので、カーネル空間とユーザー空間の間でデータを往復コピーすることは間違いなく無駄であり、ゼロコピーは主にこの非効率性を解決するためのものです。

ゼロコピーテクノロジーとは何ですか?

##ゼロ コピーの主なタスクは、

CPU によるストレージ間でのデータのコピーを回避することです。主な目的は、さまざまなゼロ コピー テクノロジを使用して、回避 CPU は不要なコピーを減らすために大量のデータ コピー タスクを実行するか、この種の単純なデータ転送タスクを他のコンポーネントに実行させて、CPU を解放して他のタスクに集中させます。これにより、システム リソースをより効率的に使用できるようになります。

引用文の例に戻りましょう。データのコピー数を減らすにはどうすればよいでしょうか?明らかに焦点は、カーネル空間とユーザー空間の間で行われるデータのコピーを減らすことです。これにより、ゼロ コピーの一種 (

#) も導入され、データ転送が不要になります。ユーザー スペースを介して

mmap を使用する

##コピー数を減らす 1 つの方法読み取り呼び出しの代わりに mmap() を呼び出すことです:

buf = mmap(diskfd, len);
write(sockfd, buf, len);
ログイン後にコピー

アプリケーションは mmap() を呼び出し、ディスク上のデータは DMA を通じてカーネル バッファにコピーされます。カーネル バッファはアプリケーションと共有されるため、カーネル バッファの内容をユーザー空間にコピーする必要はありません。次に、アプリケーションは write() を呼び出し、オペレーティング システムはカーネル バッファの内容を socket バッファに直接コピーします。これはすべてカーネル状態で発生します。ソケット バッファはデータをネットワーク カードに送信します。 同様に、図を見ると非常に簡単です:

Linux でのいくつかのゼロコピー テクノロジと適用可能なシナリオについて話しましょう

read の代わりに mmap を使用すると、明らかに 1 つのコピーが減ります。コピーされるデータの量が多い場合、間違いなく改善されます。効率。ただし、

mmap の使用にはコストがかかります。 mmap を使用すると、いくつかの隠れた罠に遭遇する可能性があります。たとえば、プログラム map がファイルをマップしているときに、そのファイルが別のプロセスによって切り捨てられる (truncate) 場合、書き込みシステム コールは SIGBUS シグナルによって終了します。不正なアドレスです。 SIGBUS シグナルは、デフォルトでプロセスを強制終了し、coredump を生成します。この方法でサーバーが停止すると、損失が発生します。

通常我们使用以下解决方案避免这种问题:

1、为SIGBUS信号建立信号处理程序
当遇到SIGBUS信号时,信号处理程序简单地返回,write系统调用在被中断之前会返回已经写入的字节数,并且errno会被设置成success,但是这是一种糟糕的处理办法,因为你并没有解决问题的实质核心。

2、使用文件租借锁
通常我们使用这种方法,在文件描述符上使用租借锁,我们为文件向内核申请一个租借锁,当其它进程想要截断这个文件时,内核会向我们发送一个实时的RT_SIGNAL_LEASE信号,告诉我们内核正在破坏你加持在文件上的读写锁。这样在程序访问非法内存并且被SIGBUS杀死之前,你的write系统调用会被中断。write会返回已经写入的字节数,并且置errno为success。

我们应该在mmap文件之前加锁,并且在操作完文件后解锁:

if(fcntl(diskfd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
    perror("kernel lease set signal");
    return -1;
}
/* l_type can be F_RDLCK F_WRLCK  加锁*/
/* l_type can be  F_UNLCK 解锁*/
if(fcntl(diskfd, F_SETLEASE, l_type)){
    perror("kernel lease set type");
    return -1;
}
ログイン後にコピー

使用sendfile#####

从2.1版内核开始,Linux引入了sendfile来简化操作:

#include<sys>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);</sys>
ログイン後にコピー

系统调用sendfile()在代表输入文件的描述符in_fd和代表输出文件的描述符out_fd之间传送文件内容(字节)。描述符out_fd必须指向一个套接字,而in_fd指向的文件必须是可以mmap的。这些局限限制了sendfile的使用,使sendfile只能将数据从文件传递到套接字上,反之则不行。
使用sendfile不仅减少了数据拷贝的次数,还减少了上下文切换,数据传送始终只发生在kernel space

Linux でのいくつかのゼロコピー テクノロジと適用可能なシナリオについて話しましょう

在我们调用sendfile时,如果有其它进程截断了文件会发生什么呢?假设我们没有设置任何信号处理程序,sendfile调用仅仅返回它在被中断之前已经传输的字节数,errno会被置为success。如果我们在调用sendfile之前给文件加了锁,sendfile的行为仍然和之前相同,我们还会收到RT_SIGNAL_LEASE的信号。

目前为止,我们已经减少了数据拷贝的次数了,但是仍然存在一次拷贝,就是页缓存到socket缓存的拷贝。那么能不能把这个拷贝也省略呢?

借助于硬件上的帮助,我们是可以办到的。之前我们是把页缓存的数据拷贝到socket缓存中,实际上,我们仅仅需要把缓冲区描述符传到socket缓冲区,再把数据长度传过去,这样DMA控制器直接将页缓存中的数据打包发送到网络中就可以了。

总结一下,sendfile系统调用利用DMA引擎将文件内容拷贝到内核缓冲区去,然后将带有文件位置和长度信息的缓冲区描述符添加socket缓冲区去,这一步不会将内核中的数据拷贝到socket缓冲区中,DMA引擎会将内核缓冲区的数据拷贝到协议引擎中去,避免了最后一次拷贝。

Linux でのいくつかのゼロコピー テクノロジと適用可能なシナリオについて話しましょう

不过这一种收集拷贝功能是需要硬件以及驱动程序支持的。

使用splice#####

sendfile只适用于将数据从文件拷贝到套接字上,限定了它的使用范围。Linux在2.6.17版本引入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);</fcntl.h>
ログイン後にコピー

splice调用在两个文件描述符之间移动数据,而不需要数据在内核空间和用户空间来回拷贝。他从fd_in拷贝len长度的数据到fd_out,但是有一方必须是管道设备,这也是目前splice的一些局限性。flags参数有以下几种取值:

  • SPLICE_F_MOVE: データをコピーするのではなく、データを移動してみてください。これはカーネルに対するちょっとした注意事項です。カーネルが pipe からデータを移動できない場合、または pipe のキャッシュがフル ページでない場合でも、データをコピーする必要があります。 Linux の初期実装にはいくつかの問題があったため、このオプションは 2.6.21 からは機能しなくなり、後の Linux バージョンで実装する必要があります。
  • ** SPLICE_F_NONBLOCK** : splice 操作はブロックされません。ただし、ファイル記述子がノンブロッキング I/O 用に設定されていない場合は、splice の呼び出しがブロックされる可能性があります。
  • ** SPLICE_F_MORE**: 後続の splice 呼び出しにはさらに多くのデータが含まれます。

スプライス呼び出しは、Linux によって提案されたパイプ バッファー メカニズムを利用するため、少なくとも 1 つの記述子がパイプである必要があります。

上記のゼロコピー テクノロジはすべて、ユーザー空間とカーネル空間の間のデータのコピーを削減することによって実装されていますが、場合によっては、ユーザー空間とカーネル空間の間でデータをコピーする必要があります。現時点では、ユーザー空間とカーネル空間でのデータコピーのタイミングについてのみ作業できます。 Linux は通常、システム オーバーヘッドを削減するために copy on write(コピー オン ライト) を使用します。このテクノロジは、COW と呼ばれることがよくあります。

紙面の都合上、この記事ではコピーオンライトについては詳しく紹介しません。一般的な説明は次のとおりです: 複数のプログラムが同時に同じデータにアクセスする場合、各プログラムはそのデータへのポインタを持ちます。各プログラムの観点から見ると、このデータは独立して所有されます。データの内容が変更されると、そのデータの内容はプログラム自身のアプリケーション空間にコピーされ、初めてそのデータはプログラムのプライベート データになります。プログラムがデータを変更する必要がない場合、データを独自のアプリケーション空間にコピーする必要はありません。これにより、データのコピーが削減されます。執筆中にコピーしたコンテンツは、別の記事を書くために使用できます。 。 。

さらに、ゼロコピー テクノロジがいくつかあります。たとえば、従来の Linux I/O に O_DIRECT マークを追加すると、直接 I/O が可能になり、自動キャッシュと、未熟な fbufs テクノロジが必要です。この記事では、すべてのゼロコピー テクノロジをカバーしているわけではありません。一般的なテクノロジをいくつか紹介するだけです。興味がある場合は、自分で勉強してください。一般的に、成熟したサーバー プロジェクトも独自のカーネルを変更し、システムの I/O 部分のデータ転送速度が向上します。

推奨チュートリアル: 「Linux 運用と保守

以上がLinux でのいくつかのゼロコピー テクノロジと適用可能なシナリオについて話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:jianshu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!