20

元のvmsplice()実装で、パイプに収まる最大ページ数の2倍のユーザーランドバッファーがある場合、バッファーの後半でvmsplice()が成功すると、カーネルがバッファの前半。

しかし、それは結局のところ真実ではありませんでした。特にTCPの場合、カーネルページは反対側からACKを受信するまで保持されていました。これを修正することは将来の作業として残されたため、TCPの場合、カーネルはパイプからページをコピーする必要があります。

vmsplice()ある種のこれに対処するオプションがありSPLICE_F_GIFTますが、問題は、カーネルから新しいページを効率的に取得する方法と、キャッシュのゴミ箱を減らす方法という、他の2つの問題を明らかにすることです。最初の問題は、mmapがカーネルにページをクリアすることを要求することであり、2番目の問題は、mmapがカーネルで派手なkscrubd機能を使用するかもしれないが、プロセスのワーキングセットを増やすことです(キャッシュトラッシング)。

これに基づいて、私はこれらの質問があります:

  • ページの安全な再利用についてユーザーランドに通知するための現在の状態は何ですか?私は特にソケット(TCP)にsplice()されたページに興味があります。過去5年間に何かが起こりましたか?
  • // mmap/ TCPサーバーでのゼロコピーの現在のベストプラクティスですか、それとも今日より良いオプションがありますか?vmsplicesplicemunmap
4

1 に答える 1

5

はい、TCPソケットが不確定な時間ページを保持しているため、サンプルコードで説明されているダブルバッファリングスキームを使用することはできません。また、私のユースケースでは、ページは循環バッファから取得されるため、ページをカーネルにギフトとして提供したり、新しいページを割り当てたりすることはできません。受信したデータにデータ破損が見られることを確認できます。

TCPソケットの送信キューのレベルを0になるまでポーリングすることにしました。これによりデータの破損が修正されますが、送信キューを0に排出するとスループットに影響するため、最適ではありません。

n = ::vmsplice(mVmsplicePipe.fd.w, &iov, 1, 0);
while (n) {
    // splice pipe to socket
    m = ::splice(mVmsplicePipe.fd.r, NULL, mFd, NULL, n, 0);
    n -= m;
}

while(1) {
    int outsize=0;
    int result;

    usleep(20000);

    result = ::ioctl(mFd, SIOCOUTQ, &outsize);
    if (result == 0) {
        LOG_NOISE("outsize %d", outsize);
    } else {
        LOG_ERR_PERROR("SIOCOUTQ");
        break;
    }
    //if (outsize <= (bufLen >> 1)) {
    if (outsize == 0) {
        LOG("outsize %d <= %u", outsize, bufLen>>1);
        break;
    }
};
于 2011-07-22T19:23:02.243 に答える