複合システムコールのようなものが実装されていないのはなぜですか?

複合システムコールのようなものが実装されていないのはなぜですか?

Syscall(システムコール)は、カーネルとユーザースペースの間の分離によって一部のパフォーマンスが低下します。したがって、システムコールを減らすのは良い考えのようです。

だから私の考えは、システムコールを1つにまとめることができるということです。したがって、アイデアは、システムコールとパラメータをメモリ内の単純なデータ構造に配置することです。その後、このデータ構造を提供する新しいシステム呼び出しを導入できます。その後、カーネルはすべての機能を並列にトリガーし、1つ(またはすべて)のシステム呼び出しが完了したらスレッドを再開できます。

私はこのアプローチが同時プログラミング(非同期I / O)のための良い基盤になり、すべてのシステムコールで同時実行を可能にし、コンテキスト全体の移行を減らすことによって既存の選択/ポーリング/エポーリングソリューションを改善すると思います。

なぜこれが行われていないのですか?

答え1

これはすでに存在します。 Linuxでは、実装は次のとおりです。io_uring、カーネルバージョン5.1(2019年5月)から利用可能:ジョブはキュー(またはより正確にはリング)に配置され、システムコールなしで処理され、結果は別のキューに送信されます。

答え2

一般的な概念が完了し、存在します。 Stephen Kittの答えからわかるように、最も近い例はLinuxのio_uringですが、そのようなインターフェースの唯一の例ではありません。 Windows、Solaris、AmigaOS、および少数の他のオペレーティングシステムには、io_uringと同様に動作する同様のIO指向の完了キューメカニズムがあります(Linuxは実際には少し遅れています)。

また、実際にはUNIXファミリーシステムでは提案されているようには機能しませんが、避けるべきシステムコールがたくさんあります。たくさん潜在的なコンテキスト切り替えは、通常、ユーザー空間で実行される特定の操作をカーネルにプッシュすることによって実装されます。システムsendfile()コールは、おそらくこのタイプのシステムコールの最良の例です。これは非常に一般的な操作(あるファイルディスクリプタから別のファイルディスクリプタへの大量のデータコピー)を実行し、それをカーネルモードにプッシュすることで、ループとコンテキストの遷移を完全に防止します。これを行うには、ユーザースペース(および追加のバッファ)が必要です。

しかし、ここで理解する必要がある重要な点は、これが意味を持つためには、関連するワークセットに関連するすべてをこのように大量に設定するコストが、「一般的な」方法で実行するコストよりも低くなければならないことです。 。 io_uringを使用することは、処理中の場合にのみ意味があります。場所IO(たとえば、VM用のブロックストレージデバイスをシミュレートする場合)(QEMUはこれを使用することをサポートし、高速ホストハードウェアでもパフォーマンスの違いは小さい)クレイジー)または毎秒数千のファイルを読みます(私が働いている会社は最近、これらのワークロードでio_uringを使用する可能性について内部的に議論し始めました)。同様にsendfile()、ユーザー空間を介してデータをコピーするために複数の読み取り/書き込み反復が必要な場合にのみ意味があります(通常は読み取り/書き込み反復を実行するのではなく、ユーザー空間でバッファスペースを余裕がない機能にもかかわらず)。高速)繰り返し)。

さらに、システムコールは実際にバッチコンテキストで意味がなければなりません。処理が呼び出し順序を維持する場合、IOは通常ここでは意味がありますが、多くは意味がありません。たとえば、このタイプのインターフェイスexec()(forkとexecの組み合わせ)を試してみてください。おそらく、しかし普通のランナーではない)。同様に、特定のタイプのシステムコールは、個別に処理される場合にのみ便利です。操作プロセスの信号マスキングは良い例です。初期設定に加えて、ほとんど常にコードの重要な部分を保護するためにこれを行い、通常はこの目的のために時期的かつ予測可能な処理が必要です。

答え3

これらの機能はかなり長い間使用されてきました。

1997年 ソラリス 2.6これを行うために、カーネル非同期IOシステムコールを追加しました。kaio()

アクセスする方法の1つは、次の方法です。`lio_listio() 関数:

lio_listio

  • リスト指向I/O

要約

cc [ flag... ] file... -lrt [ library... ]
#include <aio.h>

int lio_listio(int mode, struct aiocb *restrict const list[], int nent,
     struct sigevent *restrict sig);

説明する

このlio_listio()関数を使用すると、呼び出しプロセス、LWP、またはスレッドが単一の関数呼び出しでI / O要求のリストを開始できます。

Illumoslibcソースコードはオープンソースであり、もともとSolarisの実装から派生しており、次の場所lio_listio()にあります。https://github.com/illumos/illumos-gate/blob/470204d3561e07978b63600336e8d47cc75387fa/usr/src/lib/libc/port/aio/posix_aio.c#L121

これらの機能が一般的ではない理由の1つは、ソフトウェアとハ​​ードウェアシステム全体がそれを利用するように設計されていない限り、実際にパフォーマンスを大幅に向上させないことです。

ストレージは正しく整列されたブロックを提供するように構成され、ファイルシステムはストレージシステムによって提供されたブロックと正しく整列するように構築されなければなりません。いいえIOの問題 - すべてIOが正しく整列する必要があります。

ロータリーディスクを使用すると、同じディスクに対する一括IO操作が互いに簡単に干渉する可能性があり、ヘッドがスキャンするのに多くの時間を費やすため、実際にすべての作業速度が遅くなる可能性があります。

私の経験では、レイヤーの1つだけが間違った操作を実行すると、バッチシステム呼び出しのパフォーマンス上の利点がオーバーヘッドによって失われます。最悪のシステムコールオーバーヘッドに比べてIOが遅いからだ。

一括IOシステムコールを介して提供されるパフォーマンスの向上を利用するために、組み合わせたハードウェア/ソフトウェアシステムを作成および保守するためのコストはかなりです。

私が見た最高の数値は、多くのIO呼び出しを単一のシステム呼び出しにまとめると、パフォーマンスが約25〜30%向上する可能性があることです。

これは、24時間を通して数百ギガバイトのデータを処理する場合に重要です。

猫のビデオ視聴遅延時間を8msから6msに減らすために、このようなシステム全体を構築して維持しますか?それほど多くはありません。

関連情報