spliceシステム呼び出し実装TCPエージェント


Linusが言ったようにspliceは実際にはカーネル空間のread/writeであり、teeはカーネル空間のmemcpyであり、sendfileについてはpage cacheを使用できるファイルシステムに有効な特定の最適化にすぎない.Linusはbufferを繰り返し強調し、「なぜspliceもteeもパイプに依存するのか」について答えます.つまり、ファイル記述子のIOはbufferに依存しています.sendfileはなぜbufferを使用していないのかというと、page cacheに適用されるファイル記述子だけがsendfileを使用することができ、page cache自体がbufferとして使用できるからです.なぜシステム呼び出しができず、1つのsocketのデータを直接別のsocketに送信できるのでしょうか.できないというのではなく、APIの安定性のために、socketというファイルのために単独でspliceを作ることはできないに違いない.より合理的には、任意のファイル記述子であってもよいspliceを実現することである.しかしこれでfile_operations構造体に任意のファイルに適したフック関数splice from/toを追加します.これはできないのではなく、1つのファイルと別のファイルをspliceで関連付けます.例えば、1つのsocketから1つの文字デバイスに書きます.第1に、socketはその文字デバイスがどのように書き込まれているかを知っています.第2に、文字デバイスはsocketがどのように読み取られているかを知っています.従って正規のspliceはbufferを用い,これがpipeである.要するに、2つのファイル間のコンテンツ通信に関わると、必然的に1つのbufferが必要になる.ここでの「必然」という言葉は興味深い.実現可能かどうかの考慮ではなく、性能の考慮ではなく、APIの安定性と設計の簡単性の考慮に基づいている.ユーザー状態のread/writeインタフェースにはbufferというパラメータがあり、spliceはカーネル状態であるが、bufferも必要である.カーネル状態の既成bufferは何ですか?pipeです.以下は最も簡単なspliceで実現されたTCPエージェントです.上記のものを見なければ、2回のsplice呼び出しに失望するかもしれません.結局、直接splice(sock 1,NULL,sock 2,NULL,65535,0)を作ることができますが、必然的にbufferが必要になると、カーネルbuffer、すなわちpipeを作り、pipe+spliceはbuffer+read/writeのように自分で仕事をしなくなり、管理者に変身します.命令だけを送信して、それから自分で仕事をしなくても、カーネルがコンテンツの転送を完了するのを待っていて、効果を見ていない学生たちは、あなたたちの数が小さすぎるかもしれません.結局、カーネル空間のcopyメモリとカーネル空間のユーザー状態の間でcopyメモリは異なり、一連のプロセッサ検査のほか、ページ不足処理も大きな家ですね.次はコードで、異常処理がなく、誤った判断がなく、テーマ(spliceエージェント、すなわち無極変速エージェント)に直行します.
#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc,char *argv[])
{
        int fd, client_fd, server_fd, ret;
        int yes = 1;
        struct sockaddr_in addr = {0};
        fd = socket(AF_INET,SOCK_STREAM,0);
        addr.sin_family = AF_INET;
        addr.sin_port = htons(1234);
        addr.sin_addr.s_addr = inet_addr("192.168.4.29");
        ret = bind(fd,(struct sockaddr *)(&addr),sizeof(struct sockaddr));
        ret = setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes));
        ret = listen(fd,5);
        socklen_t len = sizeof(addr);
        client_fd = accept(fd,(struct sockaddr*)(&addr),&len);
        { // , 。
                struct sockaddr_in addr = {0}, addr2= {0};
                struct in_addr x;
                inet_aton("192.168.4.28",&x);
                addr.sin_family = AF_INET;
                addr.sin_addr = x;
                addr.sin_port = htons(12345);
                server_fd = socket(AF_INET,SOCK_STREAM,0);
                ret = connect(server_fd,(struct sockaddr*)&addr,sizeof(addr));
        }
        { // 
                struct pollfd pfds[2];
                int pfd[2] = {-1};
                int ret = -1;
                pfds[0].fd = client_fd;
                pfds[0].events = POLLIN|POLLOUT;
                pfds[1].fd = server_fd;
                pfds[1].events = POLLIN|POLLOUT;
                ret = pipe(pfd);
                while (1) {
                        int efds = -1;
                        if ((efds = poll(pfds,2,-1)) < 0) {
                                return -1;
                        }
                        // splice, sock_client->pipe, pipe->sock_server
                        if(pfds[0].revents & POLLIN) {
                                int rncount = splice(pfds[0].fd, NULL, pfd[1], NULL, 65535, SPLICE_F_MORE);
                                int wncount = splice(pfd[0], NULL, pfds[1].fd, NULL, 65535, SPLICE_F_MORE);
                        }
                }
        }
}
この方法で実現した人がいるかどうか分かりません.Openだけに注目しているからです.×××,しかし、私は本当にこのような方法が頼りだと思っています.代理店には2つあるからです.
1.制御するためです.エージェントはターゲットに直接アクセスしたくないので、ACLなどの先行作業を行う必要があります.フローコントロールなどです.2.もう一つは制御から抜け出すためだ.内部ユーザが勝手に外部にアクセスしないようにポートを閉じましたが、エージェントを使用して外部にアクセスできます.
それ以外に、エージェントは1つのトランスミッタで、転送機能として、以上の2点とは関係なく、以上の2点は、whileの前に完全に完成することができます!