无法从 C 中的管道读取数据?

Cannot perform reading from pipe in C?

我正在编写代码来与涉及读取和写入应用程序的应用程序进行交互。这是代码:第一个 - 即 input.c 与第二个交互 - 即 app.c

//input.c    
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>

#define WRITE 1
#define READ 0

void error(char* msg){
    perror(msg);
    exit(-1);
}

void got_here(char* msg){
    printf("Got_here:%s\n",msg);
}

int main(int argc, char** argv, char** envp){
    int fd_parent[2];
    int fd_child[2]; // for parent and child to write respectively
    if(pipe(fd_parent) < 0 | pipe(fd_child) < 0){
        error("Fail to create a pipe"); /// just an error-handle function
    }
    pid_t child = fork();
    if(child < 0){
        error("Fail to create a child"); 
    }
    else if(child == 0){
        dup2(fd_child[WRITE], STDOUT_FILENO);
        dup2(fd_parent[READ], STDIN_FILENO);
        close(fd_parent[WRITE]);
        close(fd_child[READ]);
        char str[100] = "./app";
        execve(str, argv,envp);
        close(fd_parent[READ]);
        close(fd_child[WRITE]);
        return 0;
    }
    else{
        close(fd_parent[READ]);
        close(fd_child[WRITE]);

        FILE* stream = fdopen(fd_child[READ], "r");
        FILE* stream_write = fdopen(fd_parent[WRITE], "w");

        char str[20];
        char menu[4] = "10\n";
        fread(str,sizeof(char), 20, stream); // Here is where the problem lies
        got_here("after read");  // it does not get here
        fwrite(menu, sizeof(char), 3, stream_write);
        fflush(stream_write);

        fclose(stream);
        fclose(stream_write);
        printf("Parent Done\n");
        return 0;
    }
}

这是应用程序代码(为了缩短代码,我只包括主要内容):

int main(int argc, char** argv, char** envp){
    char str[10];
    printf("- Select Menu -1\n");
    printf("1. Play Lotto\n");
    scanf("%s", str);
    return 0;
}

在 运行 之后,我的程序只是暂停在 fread() 行,它应该完成对应用程序的读取和写入。有趣的是,如果我在第二个程序中省略 scanf()printf(),它就可以正常工作。我尝试更改 fwritefread 的位置,但问题仍然存在。我认为这是与缓冲区相关的问题,但我尝试与之交互的应用程序不允许更改,因此我不能包含 fflush 或其他内容。

我的猜测是对的还是另有解释?以及如何克服这个问题?

您可以使用stdbuf命令修改管道另一端程序的缓冲选项。为了在 C 中执行此操作,您可以编写:

char str[100] = "./app";

char **new_argv = malloc (sizeof (char *) * (argc + 9));
new_argv[0] = "stdbuf";
new_argv[1] = "-i";
new_argv[2] = "0";
new_argv[3] = "-o";
new_argv[4] = "L";
new_argv[5] = "-e";
new_argv[6] = "L";
new_argv[7] = str;
memcpy (&new_argv[8], &argv[1], argc - 1);
new_argv[argc + 8] = NULL;

execvp ("stdbuf", new_argv);
error ("execvp");

或者如果您真的不需要将父项的参数传递给子项,则:

execlp ("stdbuf", "stdbuf", "-i", "0", "-o", "L", "-e", "L", "./app", NULL);
error ("execlp");

stdbuf 命令使用 LD_PRELOAD 将库 (libstdbuf.so) 加载到另一个程序中。这个库做了修改缓冲选项的技巧。您可以避免使用 stdbuf 并在 exec() 之前自行设置预加载选项。您也可以编写自己的库并预加载它。但是使用 stdbuf 可能是最简单的选择,如果你有这个命令可用的话。

另见 stdbuf source code

这是您的代码的完整修改示例:

//input.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>

#define WRITE 1
#define READ 0

void error (char *msg)
{
    perror (msg);
    exit (-1);
}

void got_here (char *msg)
{
    printf ("Got_here: %s\n", msg);
}

int main (int argc, char **argv, char **envp)
{
    int fd_parent[2];
    int fd_child[2]; // for parent and child to write respectively
    pid_t child;

    if (pipe (fd_parent) < 0) {
        error ("pipe(fd_parent)");
    }

    if (pipe (fd_child) < 0) {
        error ("pipe(fd_child)");
    }

    child = fork ();

    if (child < 0) {

        error ("fork");

    } else if (child == 0) {

        dup2 (fd_child[WRITE], STDOUT_FILENO);
        dup2 (fd_parent[READ], STDIN_FILENO);
        close (fd_parent[WRITE]);
        close (fd_child[READ]);

        char str[100] = "./app";

        char **new_argv = malloc (sizeof (char *) * (argc + 9));
        new_argv[0] = "stdbuf";
        new_argv[1] = "-i";
        new_argv[2] = "0";
        new_argv[3] = "-o";
        new_argv[4] = "L";
        new_argv[5] = "-e";
        new_argv[6] = "L";
        new_argv[7] = str;
        memcpy (&new_argv[8], &argv[1], argc - 1);
        new_argv[argc + 8] = NULL;

        execvp ("stdbuf", new_argv);
        error ("execvp");

        close (fd_parent[READ]);
        close (fd_child[WRITE]);

        return 0;

    } else {

        close (fd_parent[READ]);
        close (fd_child[WRITE]);

        FILE *stream = fdopen (fd_child[READ], "r");
        FILE *stream_write = fdopen (fd_parent[WRITE], "w");

        char str[20];
        char menu[4] = "10\n";
        int res = fread (str, sizeof (char), 20, stream); // Here is where the problem lies
        printf ("res = %d\n", res);
        got_here ("after read");  // it does not get here
        fwrite (menu, sizeof (char), 3, stream_write);
        fflush (stream_write);

        fclose (stream);
        fclose (stream_write);
        printf ("Parent Done\n");

        return 0;

    }
}