【问题标题】:Blocking read until specified number bytes arrived阻塞读取直到指定数量的字节到达
【发布时间】:2011-11-15 13:05:20
【问题描述】:

我需要使用“读取”等待音频编解码器设备上的缓冲区已满。为了方便起见,我们举个类似的例子:

 fd= read(fileno(stdin), &buf, 10);

当我在标准输入中输入 10 个字符时,如何从读取中返回? (我希望如果这成功了,我可以在编解码器上等待,直到指定的数据字节到达)。

上面的示例需要来自控制台的“Enter Key”,因为我希望“读取”仅在到达所需的数据字节时才解除阻塞。

编辑:要求是使用单个“读取”等待指定字节到达。

【问题讨论】:

  • 除了那个例子不是那么相似。
  • 操作控制台与简单地从标准输入读取非常不同。除非您将 tty 置于原始模式,否则 tty 不会向程序发送任何内容,直到您按回车键。如果您的问题是关于从设备读取,那么您只会在担心键盘的情况下混淆。
  • 所以这意味着我需要检查我的编解码器设备在“读取”时的行为。阻塞是底层设备的属性,而不是“读取”系统调用的属性。这就是你的意思?
  • 看看termios - 这是一个巨大的主题,但一旦你开始工作就会非常有趣

标签: c linux file-io


【解决方案1】:

这个怎么样:

#define BUFFER_SIZE 10
char buffer[BUFFER_SIZE];

/* ... */

size_t total_read = 0;
size_t total_left = BUFFER_SIZE;  /* The total size of the buffer */
char *buffer_pointer = buffer;    /* buffer is where to store the data */
while (total_left > 0)
{
    ssize_t current = read(STDIN_FILENO, buffer_pointer, total_left);
    if (current <= 0)
    {
        /* Error or end of file */
        if (current < 0)
            perror("read");  /* Error! */
        break;
    }
    else
    {
        total_read += current;     /* We have read some more data */
        total_left -= current;     /* Less data left to read */
        buffer_pointer += current; /* So we don't read over already read data */
    }
}

printf("Received %ld characters\n", total_read);
for (unsigned int i = 0; i < total_read; i++)
    printf("Character #%d: '%c'\n", i, buffer[i]);

请注意,这将阻塞您的程序,直到读取所有数据。读取的字符数可能比所有字符都少,因为可能有错误或用户按下了CTRL-D(文件结束)。

另请注意,STDIN_FILE 文件描述符很可能连接到 tty,这意味着它可能会被缓冲,因此在换行之前不会返回数据,并且您可能必须使 tty 无缓冲。

编辑 要确保连接到 stdin 的 tty 没有缓冲,请使用以下代码:

#include <termios.h>

/* ... */

/* Somewhere before reading from stdin... */
struct termios tty_settings;

tcgetattr(STDIN_FILENO, &tty_settings);
tty_settings.c_lflag &= ~ICANON;
tcsetattr(STDIN_FILENO, TCSANOW, &tty_settings);

有关tcsetattr 函数和ICANON 标志的更多信息,请查看tcsetattr 的手册页。

【讨论】:

  • 谢谢。但这并不能解决我在读取指定字节之前在单次读取时阻塞的最终目的。我认为正如 William Pursell 指出的那样,它是一个依赖于设备的属性。
  • @LunarMushrooms 这就是您将 total_left 变量初始化为的内容。如果将其初始化为 10(如我的示例中所示),则读取循环将阻塞,直到您读取 10 个字节。如果total_read 小于请求的字节数,那么它要么是错误要么是EOF。
  • 标准输入/输出就是这种情况。但是当您从特殊设备(如声卡)读取时,它会在给定字节完全读取之前返回。它取决于设备。这是关于你如何编写设备驱动程序的。
  • @LunarMushrooms 这就是为什么我把它放在一个循环中。
  • 我的要求是阻止 N 个字节的行为。不是读取数据的方法。
【解决方案2】:
do {ioctl(fd_port, FIONREAD, &bytes);} while (bytes < size);

在发出 read() 之前可能会有所帮助。

【讨论】:

  • 您是说 I_NREAD 吗?这是一个有趣的解决方案,但它似乎并没有改进“一些程序员老兄”的早期解决方案 - 我认为想要避免发出多个 read() 系统调用的唯一原因是避免系统调用开销,但是ioctl() 也是一个系统调用...
猜你喜欢
  • 1970-01-01
  • 2011-10-31
  • 2019-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-30
相关资源
最近更新 更多