libuv与标准输入重定向行为不一致

时间:2014-06-21 00:50:52

标签: c libuv

我正在开发一个基于libuv的小程序。该程序应从标准输入读取用户给定的文本,并根据输入提供结果。以下是该文件的源代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <uv.h>

static void
alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
{
    static char buffer[1 << 16];
    *buf = uv_buf_init(buffer, 1 << 16);
}

static void
read_stdin(uv_stream_t *stream, ssize_t nread, const uv_buf_t* buf)
{
    printf("Read STDIN\n");
    if (nread < 0) {
        // stdin closed
        uv_read_stop(stream);
        return;
    }
    printf("Input length: %zu\n", nread);
    printf("Buffer length: %zu (%s)\n", buf->len, buf->base);
}

int
main(int argc, char *argv[])
{
    /* Initialize loop */
    uv_loop_t loop;
    uv_loop_init(&loop);

    /* Input */
    uv_tty_t input;
    uv_tty_init(&loop, &input, STDIN_FILENO, 1);
    uv_read_start((uv_stream_t *)&input, alloc_buffer, read_stdin);

    /* Run loop */
    uv_run(&loop, UV_RUN_DEFAULT);

    return 0;
}

上述程序在编译和调用后没有标准输入重定向就可以正常工作。例如:

> ./test
hello world
Read STDIN
Input length: 12
Buffer length: 65536 (hello world
)
Read STDIN
>>> stdin closed

管道echo或其他一些过程的结果也会产生正确的结果:

> echo "hello world" | ./test
Read STDIN
Input length: 12
Buffer length: 65536 (hello world
)
Read STDIN
>>> stdin closed

当我尝试从某些常规文件或/dev/null重定向标准输入时出现问题:

> ./test < text.txt
Aborted (core dumped)

以下是崩溃的gdb回溯:

#0  0x00007ffff6df2d67 in raise () from /usr/lib/libc.so.6
#1  0x00007ffff6df4118 in abort () from /usr/lib/libc.so.6
#2  0x00007ffff79c6c90 in uv.io_poll () from /usr/lib/libuv.so.11
#3  0x00007ffff79ba64f in uv_run () from /usr/lib/libuv.so.11
#4  0x0000000000400b4b in main (argc=1, argv=0x7fffffffe338) at main.c:40

有没有人对为什么会这样做有任何想法?我是否错误地使用了libuv API?

1 个答案:

答案 0 :(得分:3)

我尝试通过 strace 运行失败的程序,并在程序中止之前看到以下行:

epoll_ctl(5, EPOLL_CTL_ADD, 0, {EPOLLIN, {u32=0, u64=0}}) = -1 EPERM (Operation not permitted)

我做了一些进一步阅读,似乎使用带有常规文件描述符的epoll根本不起作用。在原始三个测试的前两个中,文件描述符0(标准输入)不是常规文件。在上一次测试中,shell将关闭并用输入文件text.txt替换文件描述符0。

我很可能必须使用libuv库函数uv_guess_handle,它可以用来确定STDIN_FILENO文件描述符的类型:

/*
* Used to detect what type of stream should be used with a given file
* descriptor. Usually this will be used during initialization to guess the
* type of the stdio streams.
* For isatty() functionality use this function and test for UV_TTY.
*/
UV_EXTERN uv_handle_type uv_guess_handle(uv_file file);