【问题标题】:Linux non-canonical input mode works different in Linux and WSLLinux 非规范输入模式在 Linux 和 WSL 中的工作方式不同
【发布时间】:2020-11-27 19:43:51
【问题描述】:

下面的程序演示了这个问题。当您使用clang main.c -o main 构建它时,运行并按下几个键,您会看到程序总是在 Linux 上的“Calling poll()...”处停止。现在,如果您在最新的 WSL(Linux 的 Windows 子系统)上执行相同操作,您将看到它在“调用 read()...”处停止。换句话说,在 Linux 上是 poll() 阻塞,在 WSL 上是 read()

该程序基本上将输入设置为非规范模式,VTIMEVMIN 也设置为 0。如果我正在阅读右下方的两个参考资料

然后我认为在这种情况下read() 不应该阻塞,我不应该看到程序在打印“调用 read()...”后等待。然而在 WSL 上却是这样。

现在,如果您在编译和运行时传递 -DNONBLOCK,在 Linux 和 WSL 上,程序总是在打印“Calling poll()...”后阻塞,这是预期的行为。

我想知道这是否是 WSL 上的错误,或者我误读了非规范模式文档。

程序:

// clang main.c -o main

#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

void main_loop();
bool read_until_empty();

int main()
{
#if defined(NONBLOCK)
    int old_stdin_flags = fcntl(STDIN_FILENO, F_GETFL);
    fcntl(STDIN_FILENO, F_SETFL, old_stdin_flags | O_NONBLOCK);
    printf("Enabled non-blocking mode\n");
#endif

    bool fail = false;

    int tty = open("/dev/tty", O_RDWR);
    if (tty == -1) {
        printf("Unable to open /dev/tty\n");
        fail = true;
        goto cleanup;
    }

    struct termios old_termios;
    if (tcgetattr(STDIN_FILENO, &old_termios) == -1) {
        printf("tcgetattr failed\n");
        fail = true;
        goto cleanup;
    }

    struct termios new_termios = old_termios;
    cfmakeraw(&new_termios);
    // These really need to be after cfmakeraw() !!!!
    new_termios.c_cc[VMIN] = 0;
    new_termios.c_cc[VTIME] = 0;
    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios) == -1) {
        printf("tcsetattr failed\n");
        fail = true;
        goto cleanup;
    }

    printf("Type 'q' to quit.\n");
    main_loop();

cleanup:
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_termios);

#if defined(NONBLOCK)
    fcntl(STDIN_FILENO, F_SETFL, old_stdin_flags);
    printf("Restored stdin flags\n");
#endif

    if (fail) {
        return 1;
    }
}

void main_loop()
{
    printf("main_loop\n");
    struct pollfd fds[1] = { { .fd = STDIN_FILENO, .events = POLLIN } };
    
    for (;;)
    {
        printf("Calling poll()...\n");
        int poll_ret = poll(fds, 1, -1);
        if (poll_ret > 0)
        {
            printf("stdin ready for reading\n");
            if (read_until_empty())
            {
                return;
            }
        }
    }
}

bool read_until_empty()
{
    uint8_t buf[10000];

    for (;;)
    {
        printf("Calling read()...\n");
        ssize_t n_read = read(STDIN_FILENO, buf, 10000);

        if (n_read == -1)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                printf("EAGAIN or EWOULDBLOCK\n");
                return false;
            }
            else
            {
                printf("Error %d: %s\n", errno, strerror(errno));
                return false;
            }
        }
        else if (n_read == 0)
        {
            printf("stdin is empty\n");
            return false;
        }
        else
        {
            printf("Read %zd bytes\n", n_read);
            if (buf[0] == 3 || buf[0] == 113)
            {
                return true;
            }
        }
    }
}

【问题讨论】:

    标签: c linux windows-subsystem-for-linux


    【解决方案1】:

    在问完这个问题后不久,我在 WSL 问题跟踪器中找到了错误报告:https://github.com/microsoft/WSL/issues/3507

    所以这似乎是 WSL 中的一个错误。

    【讨论】:

      猜你喜欢
      • 2019-11-30
      • 2012-06-04
      • 2014-03-31
      • 2013-11-12
      • 1970-01-01
      • 1970-01-01
      • 2013-09-03
      • 2020-10-02
      • 2019-01-09
      相关资源
      最近更新 更多