首页 > 解决方案 > C:无法从连接到子进程标准输出的管道中读取

问题描述

我正在尝试创建一个控制台音乐播放器的前端,我必须通过向它发送命令来与它交互,stdin同时从它的读取它的状态stdout,所以我为此设置了 2 个管道。向子进程发送命令stdin有效,但我无法从中读取任何数据stdoutread()始终返回 -1,errno设置为Resource temporarily unavailable)。

当在终端中使用相同的参数自行启动音乐播放器时,它会输出以下内容:

S05:05
T00:00
T00:01
T00:02
T00:03
T00:04
T00:05
...

该音乐播放器的每一行都保证是 6 行 + 换行符,因此父程序的预期输出应该是:

S05:05 T00:00 T00:01 T00:02 T00:03 T00:04 T00:05...

(当终端设置为原始模式时,换行符不起作用)

但是,根本没有从子进程中读取任何内容。关于为什么会发生这种情况以及如何解决这个问题的任何帮助将不胜感激。

这是父程序的代码:

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

int main() {
    system("/bin/stty raw");            // Set up the terminal so that stdin is non-blocking

    pid_t p;
    int pipe_stdin[2], pipe_stdout[2];

    if(pipe(pipe_stdin)) return -1;     // Initialize child stdin pipe
    if(pipe(pipe_stdout)) return -1;    // Initialize child stdout pipe

    // Make reading from the pipe non-blocking
    fcntl(pipe_stdout[0], F_SETFL, fcntl(pipe_stdout[0], F_GETFL) | O_NONBLOCK);

    if((p = fork()) == 0) {             // Create the child process
        close(pipe_stdin[1]);           // Close unnecessary pipe ends to the child
        close(pipe_stdout[0]);

        dup2(pipe_stdin[0], 0);         // Redirect stdin to a pipe
        dup2(pipe_stdout[1], 1);        // Redirect stdout to a pipe

        // Start the process
        execl("/bin/sh", "sh", "-c", "./openmpt ./spacedeb.mod 2>/dev/null", NULL);
        exit(1);
    }

    close(pipe_stdin[0]);               // Close unnecessary pipe ends to the parent
    close(pipe_stdout[1]);

    int codec_write = pipe_stdin[1], codec_read = pipe_stdout[0], playing = 1;
    char buf[8];

    while(playing) {
        // Read input from the child process (read() always returns -1 here!)
        while(read(codec_read, buf, 7) >= 0) {
            printf("%s ", buf);
        }

        // Send user data to the child process (this part works!)
        switch(getchar()) {
            case ' ': write(codec_write, "P\n", 2); break;  // Play & Pause
            case 13: write(codec_write, "R\n", 2); break;   // Reset
            case 27: playing = 0; break;                    // Exit
        }
    }

    close(codec_write);                 // Close the pipes
    close(codec_read);
    system("killall -9 openmpt");       // Kill the child process

    system("/bin/stty cooked");         // Reset the terminal back to its original state
}

这是播放器的代码(如有必要,这应该不是问题):

#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <libopenmpt/libopenmpt.h>
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
#include "portaudio.h"

#define BUFFERSIZE 512
#define SAMPLERATE 48000

static int16_t buffer[BUFFERSIZE * 2];

int main(int argc, char *argv[]) {
    // Initialize OpenMPT and load the module
    FILE *file = fopen(argv[1], "rb");
    openmpt_module *mod = openmpt_module_create2(openmpt_stream_get_file_callbacks(), file, NULL, NULL, NULL, NULL, NULL, NULL, NULL);  
    fclose(file);

    // Output the length of the tune
    printf("T%02d:%02d\n", (int)openmpt_module_get_duration_seconds(mod) / 60, (int)openmpt_module_get_duration_seconds(mod) % 60);

    // Initialize PortAudio
    PaStream *stream;
    Pa_Initialize();
    Pa_OpenDefaultStream(&stream, 0, 2, paInt16, SAMPLERATE, BUFFERSIZE, NULL, NULL);
    Pa_StartStream(stream);
    
    unsigned long i, count = 0, prev = -1, paused = 0;
    char buf[3] = { 0 };

    // Set stdin to non-blocking
    fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);

    while (1) {
        // Read commands from the parent program
        while(read(0, buf, 2) >= 0) {
            switch(buf[0]) {
                case 'P':
                    paused ^= 1;
                    printf("%s--:--\n", paused ? "P" : "R");
                    break;

                case 'R':
                    openmpt_module_set_position_order_row(mod, 0, 0);
                    break;
            }
        }

        // Display the current position of the tune
        if((int)openmpt_module_get_position_seconds(mod) != prev) {
            printf("T%02d:%02d\n", (int)openmpt_module_get_position_seconds(mod) / 60, (int)openmpt_module_get_position_seconds(mod) % 60);
            prev = (int)openmpt_module_get_position_seconds(mod);
        }

        // Play audio, if not paused
        if(!paused) {
            count = openmpt_module_read_interleaved_stereo(mod, SAMPLERATE, BUFFERSIZE, buffer);
        
            if (!count) {
                break;
            }
            
            Pa_WriteStream(stream, buffer, count);
        }
    }
    
    // Tell the parent that we're done
    printf("E--:--\n");

    // Stop PortAudio
    Pa_StopStream(stream);
    Pa_CloseStream(stream);
    Pa_Terminate();
    
    // Stop OpenMPT
    openmpt_module_destroy(mod);
}

标签: c

解决方案


没关系,我自己想通了,玩家需要fflush(stdout);printf声明之后。现在一切都按预期工作。


推荐阅读