【问题标题】:Cannot perform reading from pipe in C?无法从 C 中的管道执行读取?
【发布时间】:2018-04-14 03:50:56
【问题描述】:

我正在编写一个代码来与应用程序交互,涉及到对应用程序的读写。这是代码:第一个 - 即 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 或其他内容。

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

【问题讨论】:

  • 关于:execve(str, argv,envp); 1) 如果 execve() 返回,则发生错误。所以应该在其他任何事情之前立即致电perror()。 2) 返回值 0(通常)表示成功。建议使用:exit( EXIT_FAILURE )l
  • 在没有首先检查argc 以确保实际输入命令行参数之前,永远不要访问argv[0] 以外的地方
  • 看来问题不在于execve,因为在我将perror 放入代码后它没有返回任何内容。它仍然暂停。
  • 关于:if(pipe(fd_parent) &lt; 0 | pipe(fd_child) &lt; 0) 这有点明智或。该语句应使用逻辑 OR ||
  • 包含那些内容未被使用的头文件是一种糟糕的编程习惯。建议删除这些语句:#include &lt;sys/types.h&gt; #include &lt;string.h&gt;#include &lt;errno.h&gt;

标签: c pipe stdout stdin


【解决方案1】:

您可以使用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;

    }
}

【讨论】:

  • 真的很抱歉我的回复晚了。但即使使用stdbuf 也不能解决问题。现在我真的很困惑,不知道我哪里做错了。
  • 我添加了一个完整的 input.c 示例,它适用于您提供的简单 app.c。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-05
  • 2015-07-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多