为什么当我的程序在 Linux 上运行时 splice 系统调用会失败,但在 gdb 中运行时会成功?

Why splice syscall fails when my program runs on Linux but succeeds when it runs in gdb?

我尝试运行一本书的示例代码。它的功能是接受一行输入并将它输出到标准输出和运行参数指定的文件中。 这是代码:

#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

int main( int argc, char* argv[] )
{
    if ( argc != 2 )
    {
        printf( "usage: %s <file>\n", argv[0] );
        return 1;
    }
    int filefd = open( argv[1], O_CREAT | O_WRONLY | O_TRUNC, 0666 );
    assert( filefd > 0 );

    int pipefd_stdout[2];
        int ret = pipe( pipefd_stdout );
    assert( ret != -1 );

    int pipefd_file[2];
        ret = pipe( pipefd_file );
    assert( ret != -1 );

    //the first splice()
    ret = splice( STDIN_FILENO, NULL, pipefd_stdout[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );
    assert( ret != -1 );

    ret = tee( pipefd_stdout[0], pipefd_file[1], 32768, SPLICE_F_NONBLOCK ); 
    assert( ret != -1 );

    //the second splice()       
    ret = splice( pipefd_file[0], NULL, filefd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );
    assert( ret != -1 );

    //the third splice()
    ret = splice( pipefd_stdout[0], NULL, STDOUT_FILENO, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );
    printf("errno:%d\n",errno);
    assert( ret != -1 );


    close( filefd );
        close( pipefd_stdout[0] );
        close( pipefd_stdout[1] );
        close( pipefd_file[0] );
        close( pipefd_file[1] );
    return 0;
}

编译代码后,我运行输入“123”,无法输出到stdout,第三次splice()失败,errno为22。结果截图如下: p1

当我使用 gdb 运行 代码时,它可以正常工作。这是屏幕截图: p2

内核版本:4.19.163-1

gcc 版本:10.2.0

gdb 版本:10.1

我的编译命令:g++ test.cpp -g -o LinuxServer

我的运行命令:./Linux服务器test.txt

我的 gdb 命令:gdb Linux服务器

那么,为什么当我的程序 运行 在 Linux 上时 splice 系统调用失败,但在 gdb 中 运行 时却成功?

在linuxmanual splice(2)中,有描述ERRORS: EINVAL The target file is opened in append mode.

我终端中的标准输出处于追加模式,这就是第三个拼接系统调用失败的原因。

为了解决这个问题,我们可以在拼接系统调用之前添加fcntl(STDOUT_FILENO, F_SETFL, fcntl(STDOUT_FILENO, F_GETFL) & ~O_APPEND);