为啥我的输入通过管道发送到进程时会延迟?

Posted

技术标签:

【中文标题】为啥我的输入通过管道发送到进程时会延迟?【英文标题】:Why is my input delayed when sent to process via pipes?为什么我的输入通过管道发送到进程时会延迟? 【发布时间】:2013-10-06 13:21:15 【问题描述】:

我正在为一个操作系统项目编写一个程序,它基本上是一个调制解调器键盘,因为我键入一个键,它会输出一个与该键的 ASCII 值相对应的 FSK 调制音频信号。我如何设置我的程序是它分叉一个进程并执行一个名为 minimodem 的程序(see here 了解信息)。父级设置为非规范输入模式,并让用户一次输入一个字符。然后通过管道将每个字符发送给孩子。我现在就粘贴代码:

#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <string.h>
#include <termios.h>

extern char* program_invocation_short_name;

static struct termios old, new;
void init_termios(int echo);
void reset_termios(void);
int main(int argc, char* argv[])

pid_t pid;
int my_pipe[2];

char* baud = "300";
if (argc == 2) 
    if(atoi(argv[1]) == 0) 
        printf("Use: %s [baud]\n",program_invocation_short_name);
        return EXIT_SUCCESS;
    
    baud = argv[1];


if (argc > 2) 
    printf("Too many arguments.\nUsage: %s [baud]\n",program_invocation_short_name);
    return EXIT_SUCCESS;


if (pipe(my_pipe) == -1) 
    fprintf(stderr, "%s: %s",program_invocation_short_name,strerror(errno));
    return EXIT_FAILURE;


pid = fork();
if (pid < (pid_t) 0) 
    fprintf(stderr, "%s: %s",program_invocation_short_name,strerror(errno));
    return EXIT_FAILURE;
else if (pid == (pid_t) 0) 
    /***************/
    /*CHILD PROCESS*/
    /***************/
    close(my_pipe[1]); /*Child doesn't write*/
    dup2(my_pipe[0], 0); /*Redirect stdin to read side of pipe*/
    close(my_pipe[0]); /*Close read end as it's dup'd*/
    execl("/usr/local/bin/minimodem","minimodem","--tx", baud,"-q","-A",NULL);

    fprintf(stderr, "%s: %s",program_invocation_short_name,strerror(errno));
else if (pid > (pid_t) 0) 
    /****************/
    /*PARENT PROCESS*/
    /****************/
    char c;
    close(my_pipe[0]); /*Parent doesn't read*/
    init_termios(1);
    atexit(reset_termios);

    while(1) 
        c = getchar();
        if (c == 0x03)
            break;
        if (write(my_pipe[1], &c, 1) == -1) 
            fprintf(stderr, "%s: %s",
                    program_invocation_short_name, strerror(errno));
            return EXIT_FAILURE;
        
    
    close(my_pipe[1]);

return EXIT_SUCCESS;


void init_termios(int echo)

    tcgetattr(0, &old); /*get old terminal i/o settings*/
    new = old; /*make new  settings same as old settings */
    new.c_lflag &= ~ICANON;
    new.c_lflag &= echo ? ECHO : ~ECHO; /*set appropriate echo mode*/
    tcsetattr(0, TCSANOW, &new); /*use new terminal i/o settings*/


void reset_termios(void)

    tcsetattr(0, TCSANOW, &old);

我的问题在于用户输入。打字时,似乎第一个字符被写入并生成了音频,然后有一个延迟,然后缓冲区中的其余字符会像预期的那样连续生成。如果输入中有足够大的停顿,那么它会回到开始,在中断后输入的第一个字符生成,然后是延迟,然后是预期的功能。 我的手指交叉,这不是因为 minimodem 程序不是为了以这种方式使用而编写的,而且这个问题是可以克服的。 如果有人能对此事有所了解,我将非常感激。 谢谢。

注意:我尝试将输入放入环形缓冲区,然后将该输入在单独的线程中消耗并发送给子进程。 NOOOT 更好。甚至不确定注意到这是否有效。

【问题讨论】:

我在 minimodem 的源代码中看到,只要没有足够的数据来保持音频缓冲区满,它就会显式写入 0.5 秒的静音。这就是你听到的延迟量吗? 是的!我会说那是时间。您在哪个文件和行上看到了这个?您认为添加沉默的原因是什么?你能给我一个关于如何解决这个问题的提示吗?我对修改和重新编译 minimodem 没有任何疑虑。 我写了一个答案,希望能解释这种情况。如果还不清楚,请告诉我。 【参考方案1】:

当 Minimodem 检测到没有足够的输入数据来保持音频缓冲区满时,它将写入 0.5 秒的静音。这是为了确保它写入的任何音频都连续输出。音频驱动程序或服务器(如pulseaudio)的典型情况是,音频仅以块的形式写入。当您向音频缓冲区写入的数据少于完整块时,卡不会发出声音,因为驱动程序或服务器正在等待足够的音频数据,以便它可以一次写入完整块。由于 minimodem 写入的数据通常不会匹配完整的音频块,因此您会遇到音频数据的最后一部分不会被写入的情况。为避免此问题,minimodem 会写入足够的静音以保证音频的最后一部分输出到卡。

例如,假设音频驱动程序或服务器写入 1000 字节的块。现在假设您键入一个字符,这会产生 2500 字节的音频数据。音频驱动程序或服务器将导致播放 2000 字节的数据,并在缓冲区中留下 500 字节。由于调制解调器协议需要连续的音频,因此将这 500 字节的音频数据留在缓冲区中没有任何意义。接收者不会看到完整的字符。然后接收器必须假设音频是乱码并丢弃该字符。

因此,为避免这种情况,minimodem 将写入 0.5 秒的静音,这可能是数千字节的音频数据。这将保证保留在缓冲区中的 500 字节音频数据被播放,因为肯定会有足够的音频数据来完成该块。

现在确实,缓冲区中可能会有一些额外的静音,并且不会立即播放,但这没什么大不了的,因为这不会导致任何损坏。接收器可以处理不同数量的数据之间的静音(请注意,这特定于 minimodem 的工作方式,真实模型总是会产生声音)。

可以通过手动设置块大小来改进 Minimodem。这样,它就可以准确地知道需要写入多少静音才能刷新音频,而不是写入任意数量,因此可以将延迟降至最低。

【讨论】:

感谢沃恩。您能否指出找到该功能的文件/行号?如果有帮助,我正在使用 ALSA 而不是 Portaudio。再次感谢。 我找到了。我会努力解决问题的。我只是不知道从哪里开始。祝我好运。感谢您的帮助。

以上是关于为啥我的输入通过管道发送到进程时会延迟?的主要内容,如果未能解决你的问题,请参考以下文章

当我将任何命令通过管道传输到 telnet 会话时会发生啥,为啥会话会关闭

写入管道或管道

使用命名管道向子进程发送参数

Windows IPC:我可以通过匿名管道发送二进制数据吗?

Linux。 C 中的管道。将数据发送到另一个进程。写作和阅读

为啥我将邮件内容发送到 Hotmail 帐户时会作为附件发送?