splice() 从管道到 TCP 缓冲?

发布于 2024-09-24 13:32:49 字数 5475 浏览 11 评论 0原文

来自 linuxquestions.org 的 xpost,抱歉...

我编写了一个小测试程序,看看简单的代理是否会从使用 splice() 中受益,但读取我从管道拼接到 TCP 套接字的数据总是需要 200 毫秒从插座的另一端。

下面是用于测试它的 Perl 程序:

package test_pipes_2;
use strict;
use warnings;
use IO::Handle();
use POSIX qw(:errno_h);
use Time::HiRes qw(time);
use English qw(-no_match_vars);
use IO::Socket::INET;
use Socket qw(IPPROTO_TCP TCP_NODELAY);

__PACKAGE__->run if not caller;

sub run {
    my( $class ) = @ARG;

    pipe my $parent_reader, my $child_writer;
    my $parent_reader_fd = fileno $parent_reader;
    my $child_writer_fd  = fileno $child_writer;
    pipe my $child_reader,  my $parent_writer;
    my $child_reader_fd  = fileno $child_reader;
    my $parent_writer_fd = fileno $parent_writer;

    my $server = IO::Socket::INET->new(
        LocalAddr   => '127.0.0.1:9000',
        Type        => SOCK_STREAM,
        Listen      => 5,
        ReuseAddr   => 1,
        Blocking    => 1,
    ) || die $OS_ERROR;

    my $client_browser = IO::Socket::INET->new(
        PeerAddr   => '127.0.0.1:9000',
        Type        => SOCK_STREAM,
        Blocking    => 1,
    ) || die $OS_ERROR;
#   setsockopt $client_browser, IPPROTO_TCP, TCP_NODELAY, 1;
    my $client_browser_fd = fileno $client_browser;

    my $server_browser = $server->accept() || die $OS_ERROR;
#   setsockopt $server_browser, IPPROTO_TCP, TCP_NODELAY, 1;
    my $server_browser_fd = fileno $server_browser;

    for( 1 .. 3 ) { #  100_000
        syswrite $client_browser, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n";
        $server_browser->recv( my $request1, 4096, POSIX::MSG_PEEK );

        syscall 313, $server_browser_fd, undef, $child_writer_fd, undef, 4096, 1;
        sysread $parent_reader, my $request2, 4096;

        my $response = "200 OK\n<head><title>html</title></head><body>body from $PID</body>";
        syswrite $parent_writer, $response;
        syscall 313, $child_reader_fd, undef, $server_browser_fd, undef, 4096, 1;
#       syswrite $server_browser, "\n"; # eliminates delay, adds syscall...
        sysread $client_browser, my $response1, 4096;
#       chomp $response1;

        if( $response1 ne $response ) {
            warn 'Got wrong response: ', $response1 // 'undef';
            no warnings 'once';
            $DB::single = 1;
        }
    }
}

1;

下面是 strace 输出的示例:

socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 8 <0.000010>
setsockopt(8, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 <0.000006>
bind(8, {sa_family=AF_INET, sin_port=htons(9000), sin_addr=inet_addr("127.0.0.1")}, 16) = 0 <0.000008>
listen(8, 5)                            = 0 <0.000008>
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 9 <0.000006>
connect(9, {sa_family=AF_INET, sin_port=htons(9000), sin_addr=inet_addr("127.0.0.1")}, 16) = 0 <0.000037>
accept(8, {sa_family=AF_INET, sin_port=htons(49361), sin_addr=inet_addr("127.0.0.1")}, [16]) = 10 <0.000007>
write(9, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 41) = 41 <0.000014>
recvfrom(10, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 4096, MSG_PEEK, {sa_family=0x8b60 /* AF_??? */, sa_data="(\10\2\0\300\321\177\0\0\1\0\0\0\0"...}, [0]) = 41 <0.000007>
splice(0xa, 0, 0x5, 0, 0x1000, 0x1)     = 41 <0.000007>
read(4, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 4096) = 41 <0.000005>
write(7, "200 OK\n<head><title>html</title></head><body>body from 14019</body>"..., 67) = 67 <0.000005>
splice(0x6, 0, 0xa, 0, 0x1000, 0x1)     = 67 <0.000007>
read(9, "200 OK\n<head><title>html</title></head><body>body from 14019</body>"..., 4096) = 67 <0.200331>
write(9, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 41) = 41 <0.000016>
recvfrom(10, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 4096, MSG_PEEK, {sa_family=0x8b60 /* AF_??? */, sa_data="(\10\2\0\300\321\177\0\0\1\0\0\0\0"...}, [0]) = 41 <0.000008>
splice(0xa, 0, 0x5, 0, 0x1000, 0x1)     = 41 <0.000006>
read(4, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 4096) = 41 <0.000005>
write(7, "200 OK\n<head><title>html</title></head><body>body from 14019</body>"..., 67) = 67 <0.000005>
splice(0x6, 0, 0xa, 0, 0x1000, 0x1)     = 67 <0.000006>
read(9, "200 OK\n<head><title>html</title></head><body>body from 14019</body>"..., 4096) = 67 <0.200622>
write(9, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 41) = 41 <0.000018>
recvfrom(10, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 4096, MSG_PEEK, {sa_family=0x8b60 /* AF_??? */, sa_data="(\10\2\0\300\321\177\0\0\1\0\0\0\0"...}, [0]) = 41 <0.000007>
splice(0xa, 0, 0x5, 0, 0x1000, 0x1)     = 41 <0.000006>
read(4, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 4096) = 41 <0.000005>
write(7, "200 OK\n<head><title>html</title></head><body>body from 14019</body>"..., 67) = 67 <0.000005>
splice(0x6, 0, 0xa, 0, 0x1000, 0x1)     = 67 <0.000005>
read(9, "200 OK\n<head><title>html</title></head><body>body from 14019</body>"..., 4096) = 67 <0.200649

请注意大约 200 毫秒的 read(9,...) 调用。如果我取消注释该行以发送“\n”,则不会有延迟。我做错了什么?谢谢!

xpost from linuxquestions.org, sorry...

I wrote a small test program to see if a simple proxy would benefit from using splice() but it always takes 200ms for the data that I spliced from a pipe to a TCP socket to be read from the other end of the socket.

Here is the Perl program to test it:

package test_pipes_2;
use strict;
use warnings;
use IO::Handle();
use POSIX qw(:errno_h);
use Time::HiRes qw(time);
use English qw(-no_match_vars);
use IO::Socket::INET;
use Socket qw(IPPROTO_TCP TCP_NODELAY);

__PACKAGE__->run if not caller;

sub run {
    my( $class ) = @ARG;

    pipe my $parent_reader, my $child_writer;
    my $parent_reader_fd = fileno $parent_reader;
    my $child_writer_fd  = fileno $child_writer;
    pipe my $child_reader,  my $parent_writer;
    my $child_reader_fd  = fileno $child_reader;
    my $parent_writer_fd = fileno $parent_writer;

    my $server = IO::Socket::INET->new(
        LocalAddr   => '127.0.0.1:9000',
        Type        => SOCK_STREAM,
        Listen      => 5,
        ReuseAddr   => 1,
        Blocking    => 1,
    ) || die $OS_ERROR;

    my $client_browser = IO::Socket::INET->new(
        PeerAddr   => '127.0.0.1:9000',
        Type        => SOCK_STREAM,
        Blocking    => 1,
    ) || die $OS_ERROR;
#   setsockopt $client_browser, IPPROTO_TCP, TCP_NODELAY, 1;
    my $client_browser_fd = fileno $client_browser;

    my $server_browser = $server->accept() || die $OS_ERROR;
#   setsockopt $server_browser, IPPROTO_TCP, TCP_NODELAY, 1;
    my $server_browser_fd = fileno $server_browser;

    for( 1 .. 3 ) { #  100_000
        syswrite $client_browser, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n";
        $server_browser->recv( my $request1, 4096, POSIX::MSG_PEEK );

        syscall 313, $server_browser_fd, undef, $child_writer_fd, undef, 4096, 1;
        sysread $parent_reader, my $request2, 4096;

        my $response = "200 OK\n<head><title>html</title></head><body>body from $PID</body>";
        syswrite $parent_writer, $response;
        syscall 313, $child_reader_fd, undef, $server_browser_fd, undef, 4096, 1;
#       syswrite $server_browser, "\n"; # eliminates delay, adds syscall...
        sysread $client_browser, my $response1, 4096;
#       chomp $response1;

        if( $response1 ne $response ) {
            warn 'Got wrong response: ', $response1 // 'undef';
            no warnings 'once';
            $DB::single = 1;
        }
    }
}

1;

And here is a sample of the strace output:

socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 8 <0.000010>
setsockopt(8, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 <0.000006>
bind(8, {sa_family=AF_INET, sin_port=htons(9000), sin_addr=inet_addr("127.0.0.1")}, 16) = 0 <0.000008>
listen(8, 5)                            = 0 <0.000008>
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 9 <0.000006>
connect(9, {sa_family=AF_INET, sin_port=htons(9000), sin_addr=inet_addr("127.0.0.1")}, 16) = 0 <0.000037>
accept(8, {sa_family=AF_INET, sin_port=htons(49361), sin_addr=inet_addr("127.0.0.1")}, [16]) = 10 <0.000007>
write(9, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 41) = 41 <0.000014>
recvfrom(10, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 4096, MSG_PEEK, {sa_family=0x8b60 /* AF_??? */, sa_data="(\10\2\0\300\321\177\0\0\1\0\0\0\0"...}, [0]) = 41 <0.000007>
splice(0xa, 0, 0x5, 0, 0x1000, 0x1)     = 41 <0.000007>
read(4, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 4096) = 41 <0.000005>
write(7, "200 OK\n<head><title>html</title></head><body>body from 14019</body>"..., 67) = 67 <0.000005>
splice(0x6, 0, 0xa, 0, 0x1000, 0x1)     = 67 <0.000007>
read(9, "200 OK\n<head><title>html</title></head><body>body from 14019</body>"..., 4096) = 67 <0.200331>
write(9, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 41) = 41 <0.000016>
recvfrom(10, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 4096, MSG_PEEK, {sa_family=0x8b60 /* AF_??? */, sa_data="(\10\2\0\300\321\177\0\0\1\0\0\0\0"...}, [0]) = 41 <0.000008>
splice(0xa, 0, 0x5, 0, 0x1000, 0x1)     = 41 <0.000006>
read(4, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 4096) = 41 <0.000005>
write(7, "200 OK\n<head><title>html</title></head><body>body from 14019</body>"..., 67) = 67 <0.000005>
splice(0x6, 0, 0xa, 0, 0x1000, 0x1)     = 67 <0.000006>
read(9, "200 OK\n<head><title>html</title></head><body>body from 14019</body>"..., 4096) = 67 <0.200622>
write(9, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 41) = 41 <0.000018>
recvfrom(10, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 4096, MSG_PEEK, {sa_family=0x8b60 /* AF_??? */, sa_data="(\10\2\0\300\321\177\0\0\1\0\0\0\0"...}, [0]) = 41 <0.000007>
splice(0xa, 0, 0x5, 0, 0x1000, 0x1)     = 41 <0.000006>
read(4, "HTTP/1.1 GET /foo/\r\nLocation: foo.com\r\n\r\n"..., 4096) = 41 <0.000005>
write(7, "200 OK\n<head><title>html</title></head><body>body from 14019</body>"..., 67) = 67 <0.000005>
splice(0x6, 0, 0xa, 0, 0x1000, 0x1)     = 67 <0.000005>
read(9, "200 OK\n<head><title>html</title></head><body>body from 14019</body>"..., 4096) = 67 <0.200649

Note the ~200ms read(9,...) calls. If I uncomment the line to send a "\n" then there is no delay. What am I doing wrong? Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文